<commit_before>package session

import (
	"fmt"
	"io"
	"kmud/database"
	"kmud/model"
	"kmud/utils"
	"labix.org/v2/mgo/bson"
	"strconv"
	// "log"
	// "os"
	"strings"
	"time"
)

type Session struct {
	conn   io.ReadWriter
	user   *database.User
	player *database.Character
	room   *database.Room
	zone   *database.Zone

	prompt string

	userInputChannel chan string
	inputModeChannel chan userInputMode
	panicChannel     chan interface{}
	eventChannel     chan model.Event

	silentMode bool

	// These handlers encapsulate all of the functions that handle user
	// input into a single struct. This makes it so that we can use reflection
	// to lookup the function that should handle the user's input without fear
	// of them calling some function that we didn't intent to expose to them.
	commander commandHandler
	actioner  actionHandler

	replyId bson.ObjectId

	// logger *log.Logger
}

func NewSession(conn io.ReadWriter, user *database.User, player *database.Character) *Session {
	var session Session
	session.conn = conn
	session.user = user
	session.player = player
	session.room = model.M.GetRoom(player.GetRoomId())
	session.zone = model.M.GetZone(session.room.GetZoneId())

	session.prompt = "%h/%H> "

	session.userInputChannel = make(chan string)
	session.inputModeChannel = make(chan userInputMode)
	session.panicChannel = make(chan interface{})
	session.eventChannel = model.Register(session.player)

	session.silentMode = false
	session.commander.session = &session
	session.actioner.session = &session

	// file, err := os.OpenFile(player.PrettyName()+".log", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
	// utils.PanicIfError(err)

	// session.logger = log.New(file, player.PrettyName()+" ", log.LstdFlags)

	return &session
}

type userInputMode int

const (
	CleanUserInput userInputMode = iota
	RawUserInput   userInputMode = iota
)

func toggleExitMenu(cm utils.ColorMode, room *database.Room) utils.Menu {
	onOrOff := func(direction database.ExitDirection) string {
		text := "Off"
		if room.HasExit(direction) {
			text = "On"
		}
		return utils.Colorize(cm, utils.ColorBlue, text)
	}

	menu := utils.NewMenu("Edit Exits")

	menu.AddAction("n", "[N]orth: "+onOrOff(database.DirectionNorth))
	menu.AddAction("ne", "[NE]North East: "+onOrOff(database.DirectionNorthEast))
	menu.AddAction("e", "[E]ast: "+onOrOff(database.DirectionEast))
	menu.AddAction("se", "[SE]South East: "+onOrOff(database.DirectionSouthEast))
	menu.AddAction("s", "[S]outh: "+onOrOff(database.DirectionSouth))
	menu.AddAction("sw", "[SW]South West: "+onOrOff(database.DirectionSouthWest))
	menu.AddAction("w", "[W]est: "+onOrOff(database.DirectionWest))
	menu.AddAction("nw", "[NW]North West: "+onOrOff(database.DirectionNorthWest))
	menu.AddAction("u", "[U]p: "+onOrOff(database.DirectionUp))
	menu.AddAction("d", "[D]own: "+onOrOff(database.DirectionDown))

	return menu
}

func npcMenu(room *database.Room) utils.Menu {
	npcs := model.M.NpcsIn(room)

	menu := utils.NewMenu("NPCs")

	menu.AddAction("n", "[N]ew")

	for i, npc := range npcs {
		index := i + 1
		actionText := fmt.Sprintf("[%v]%v", index, npc.PrettyName())
		menu.AddActionData(index, actionText, npc.GetId())
	}

	return menu
}

func specificNpcMenu(npcId bson.ObjectId) utils.Menu {
	npc := model.M.GetCharacter(npcId)
	menu := utils.NewMenu(npc.PrettyName())
	menu.AddAction("r", "[R]ename")
	menu.AddAction("d", "[D]elete")
	menu.AddAction("c", "[C]onversation")
	return menu
}

func (session *Session) Exec() {
	defer model.Unregister(session.eventChannel)

	session.printLineColor(utils.ColorWhite, "Welcome, "+session.player.PrettyName())
	session.printRoom()

	// Main routine in charge of actually reading input from the connection object,
	// also has built in throttling to limit how fast we are allowed to process
	// commands from the user.
	go func() {
		defer func() {
			if r := recover(); r != nil {
				session.panicChannel <- r
			}
		}()

		throttler := utils.NewThrottler(1000 * time.Millisecond)

		for {
			mode := <-session.inputModeChannel
			input := ""

			switch mode {
			case CleanUserInput:
				input = utils.GetUserInputP(session.conn, session) // TODO - Check thread safety
			case RawUserInput:
				input = utils.GetRawUserInputP(session.conn, session)
			default:
				panic("Unhandled case in switch statement (userInputMode)")
			}

			throttler.Sync()
			session.userInputChannel <- input
		}
	}()

	// Main loop
	for {
		input := session.getUserInput(RawUserInput, session.GetPrompt())
		if input == "" || input == "logout" || input == "quit" {
			return
		}

		if strings.HasPrefix(input, "/") {
			session.commander.handleCommand(utils.Argify(input[1:]))
		} else {
			session.actioner.handleAction(utils.Argify(input))
		}
	}
}

func (session *Session) printString(data string) {
	io.WriteString(session.conn, data)
}

func (session *Session) printLineColor(color utils.Color, line string, a ...interface{}) {
	utils.WriteLine(session.conn, utils.Colorize(session.user.GetColorMode(), color, fmt.Sprintf(line, a...)))
}

func (session *Session) printLine(line string, a ...interface{}) {
	session.printLineColor(utils.ColorWhite, line, a...)
}

func (session *Session) printError(err string, a ...interface{}) {
	session.printLineColor(utils.ColorRed, err, a...)
}

func (session *Session) printRoom() {
	playerList := model.M.PlayersIn(session.room, session.player)
	npcList := model.M.NpcsIn(session.room)
	session.printLine(session.room.ToString(database.ReadMode, session.user.GetColorMode(),
		playerList, npcList, model.M.GetItems(session.room.GetItemIds())))
}

func (session *Session) printRoomEditor() {
	session.printLine(session.room.ToString(database.EditMode, session.user.GetColorMode(), nil, nil, nil))
}

func (session *Session) clearLine() {
	utils.ClearLine(session.conn)
}

func (session *Session) asyncMessage(message string) {
	session.clearLine()
	session.printLine(message)
}

// Same behavior as menu.Exec(), except that it uses getUserInput
// which doesn't block the event loop while waiting for input
func (session *Session) execMenu(menu utils.Menu) (string, bson.ObjectId) {
	choice := ""
	var data bson.ObjectId

	for {
		menu.Print(session.conn, session.user.GetColorMode())
		choice = session.getUserInput(CleanUserInput, menu.GetPrompt())
		if menu.HasAction(choice) || choice == "" {
			data = menu.GetData(choice)
			break
		}
	}
	return choice, data
}

// getUserInput allows us to retrieve user input in a way that doesn't block the
// event loop by using channels and a separate Go routine to grab
// either the next user input or the next event.
func (session *Session) getUserInput(inputMode userInputMode, prompt string) string {
	session.inputModeChannel <- inputMode

	for {
		select {
		case input := <-session.userInputChannel:
			return input
		case event := <-session.eventChannel:
			if session.silentMode {
				continue
			}

			if event.Type() == model.TellEventType {
				tellEvent := event.(model.TellEvent)
				session.replyId = tellEvent.From.GetId()
			} else if event.Type() == model.CombatEventType {
				combatEvent := event.(model.CombatEvent)

				if combatEvent.Defender == session.player {
					session.player.Hit(combatEvent.Damage)
				}
			} else if event.Type() == model.TimerEventType {
				if !model.InCombat(session.player) {
					oldHps := session.player.GetHitPoints()
					newHps := session.player.Heal(5)

					if oldHps != newHps {
						session.clearLine()
						session.printString(session.GetPrompt())
					}
				}
			}

			message := event.ToString(session.player)
			if message != "" {
				session.asyncMessage(message)
				session.printString(session.GetPrompt())
			}

		case quitMessage := <-session.panicChannel:
			panic(quitMessage)
		}
	}
	panic("Unexpected code path")
}

func (session *Session) GetPrompt() string {
	prompt := session.prompt
	prompt = strings.Replace(prompt, "%h", strconv.Itoa(session.player.GetHitPoints()), -1)
	prompt = strings.Replace(prompt, "%H", strconv.Itoa(session.player.GetHealth()), -1)

	return utils.Colorize(session.user.GetColorMode(), utils.ColorWhite, prompt)
}

// vim: nocindent
<commit_msg>End combat when someone hits 0 hps<commit_after>package session

import (
	"fmt"
	"io"
	"kmud/database"
	"kmud/model"
	"kmud/utils"
	"labix.org/v2/mgo/bson"
	"strconv"
	// "log"
	// "os"
	"strings"
	"time"
)

type Session struct {
	conn   io.ReadWriter
	user   *database.User
	player *database.Character
	room   *database.Room
	zone   *database.Zone

	prompt string

	userInputChannel chan string
	inputModeChannel chan userInputMode
	panicChannel     chan interface{}
	eventChannel     chan model.Event

	silentMode bool

	// These handlers encapsulate all of the functions that handle user
	// input into a single struct. This makes it so that we can use reflection
	// to lookup the function that should handle the user's input without fear
	// of them calling some function that we didn't intent to expose to them.
	commander commandHandler
	actioner  actionHandler

	replyId bson.ObjectId

	// logger *log.Logger
}

func NewSession(conn io.ReadWriter, user *database.User, player *database.Character) *Session {
	var session Session
	session.conn = conn
	session.user = user
	session.player = player
	session.room = model.M.GetRoom(player.GetRoomId())
	session.zone = model.M.GetZone(session.room.GetZoneId())

	session.prompt = "%h/%H> "

	session.userInputChannel = make(chan string)
	session.inputModeChannel = make(chan userInputMode)
	session.panicChannel = make(chan interface{})
	session.eventChannel = model.Register(session.player)

	session.silentMode = false
	session.commander.session = &session
	session.actioner.session = &session

	// file, err := os.OpenFile(player.PrettyName()+".log", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
	// utils.PanicIfError(err)

	// session.logger = log.New(file, player.PrettyName()+" ", log.LstdFlags)

	return &session
}

type userInputMode int

const (
	CleanUserInput userInputMode = iota
	RawUserInput   userInputMode = iota
)

func toggleExitMenu(cm utils.ColorMode, room *database.Room) utils.Menu {
	onOrOff := func(direction database.ExitDirection) string {
		text := "Off"
		if room.HasExit(direction) {
			text = "On"
		}
		return utils.Colorize(cm, utils.ColorBlue, text)
	}

	menu := utils.NewMenu("Edit Exits")

	menu.AddAction("n", "[N]orth: "+onOrOff(database.DirectionNorth))
	menu.AddAction("ne", "[NE]North East: "+onOrOff(database.DirectionNorthEast))
	menu.AddAction("e", "[E]ast: "+onOrOff(database.DirectionEast))
	menu.AddAction("se", "[SE]South East: "+onOrOff(database.DirectionSouthEast))
	menu.AddAction("s", "[S]outh: "+onOrOff(database.DirectionSouth))
	menu.AddAction("sw", "[SW]South West: "+onOrOff(database.DirectionSouthWest))
	menu.AddAction("w", "[W]est: "+onOrOff(database.DirectionWest))
	menu.AddAction("nw", "[NW]North West: "+onOrOff(database.DirectionNorthWest))
	menu.AddAction("u", "[U]p: "+onOrOff(database.DirectionUp))
	menu.AddAction("d", "[D]own: "+onOrOff(database.DirectionDown))

	return menu
}

func npcMenu(room *database.Room) utils.Menu {
	npcs := model.M.NpcsIn(room)

	menu := utils.NewMenu("NPCs")

	menu.AddAction("n", "[N]ew")

	for i, npc := range npcs {
		index := i + 1
		actionText := fmt.Sprintf("[%v]%v", index, npc.PrettyName())
		menu.AddActionData(index, actionText, npc.GetId())
	}

	return menu
}

func specificNpcMenu(npcId bson.ObjectId) utils.Menu {
	npc := model.M.GetCharacter(npcId)
	menu := utils.NewMenu(npc.PrettyName())
	menu.AddAction("r", "[R]ename")
	menu.AddAction("d", "[D]elete")
	menu.AddAction("c", "[C]onversation")
	return menu
}

func (session *Session) Exec() {
	defer model.Unregister(session.eventChannel)

	session.printLineColor(utils.ColorWhite, "Welcome, "+session.player.PrettyName())
	session.printRoom()

	// Main routine in charge of actually reading input from the connection object,
	// also has built in throttling to limit how fast we are allowed to process
	// commands from the user.
	go func() {
		defer func() {
			if r := recover(); r != nil {
				session.panicChannel <- r
			}
		}()

		throttler := utils.NewThrottler(1000 * time.Millisecond)

		for {
			mode := <-session.inputModeChannel
			input := ""

			switch mode {
			case CleanUserInput:
				input = utils.GetUserInputP(session.conn, session) // TODO - Check thread safety
			case RawUserInput:
				input = utils.GetRawUserInputP(session.conn, session)
			default:
				panic("Unhandled case in switch statement (userInputMode)")
			}

			throttler.Sync()
			session.userInputChannel <- input
		}
	}()

	// Main loop
	for {
		input := session.getUserInput(RawUserInput, session.GetPrompt())
		if input == "" || input == "logout" || input == "quit" {
			return
		}

		if strings.HasPrefix(input, "/") {
			session.commander.handleCommand(utils.Argify(input[1:]))
		} else {
			session.actioner.handleAction(utils.Argify(input))
		}
	}
}

func (session *Session) printString(data string) {
	io.WriteString(session.conn, data)
}

func (session *Session) printLineColor(color utils.Color, line string, a ...interface{}) {
	utils.WriteLine(session.conn, utils.Colorize(session.user.GetColorMode(), color, fmt.Sprintf(line, a...)))
}

func (session *Session) printLine(line string, a ...interface{}) {
	session.printLineColor(utils.ColorWhite, line, a...)
}

func (session *Session) printError(err string, a ...interface{}) {
	session.printLineColor(utils.ColorRed, err, a...)
}

func (session *Session) printRoom() {
	playerList := model.M.PlayersIn(session.room, session.player)
	npcList := model.M.NpcsIn(session.room)
	session.printLine(session.room.ToString(database.ReadMode, session.user.GetColorMode(),
		playerList, npcList, model.M.GetItems(session.room.GetItemIds())))
}

func (session *Session) printRoomEditor() {
	session.printLine(session.room.ToString(database.EditMode, session.user.GetColorMode(), nil, nil, nil))
}

func (session *Session) clearLine() {
	utils.ClearLine(session.conn)
}

func (session *Session) asyncMessage(message string) {
	session.clearLine()
	session.printLine(message)
}

// Same behavior as menu.Exec(), except that it uses getUserInput
// which doesn't block the event loop while waiting for input
func (session *Session) execMenu(menu utils.Menu) (string, bson.ObjectId) {
	choice := ""
	var data bson.ObjectId

	for {
		menu.Print(session.conn, session.user.GetColorMode())
		choice = session.getUserInput(CleanUserInput, menu.GetPrompt())
		if menu.HasAction(choice) || choice == "" {
			data = menu.GetData(choice)
			break
		}
	}
	return choice, data
}

// getUserInput allows us to retrieve user input in a way that doesn't block the
// event loop by using channels and a separate Go routine to grab
// either the next user input or the next event.
func (session *Session) getUserInput(inputMode userInputMode, prompt string) string {
	session.inputModeChannel <- inputMode

	for {
		select {
		case input := <-session.userInputChannel:
			return input
		case event := <-session.eventChannel:
			if session.silentMode {
				continue
			}

			if event.Type() == model.TellEventType {
				tellEvent := event.(model.TellEvent)
				session.replyId = tellEvent.From.GetId()
			} else if event.Type() == model.CombatEventType {
				combatEvent := event.(model.CombatEvent)

				if combatEvent.Defender == session.player {
					session.player.Hit(combatEvent.Damage)
					if session.player.GetHitPoints() <= 0 {
						session.asyncMessage(">> You're dead <<")
						model.StopFight(combatEvent.Defender)
						model.StopFight(combatEvent.Attacker)
					}
				}
			} else if event.Type() == model.TimerEventType {
				if !model.InCombat(session.player) {
					oldHps := session.player.GetHitPoints()
					newHps := session.player.Heal(5)

					if oldHps != newHps {
						session.clearLine()
						session.printString(session.GetPrompt())
					}
				}
			}

			message := event.ToString(session.player)
			if message != "" {
				session.asyncMessage(message)
				session.printString(session.GetPrompt())
			}

		case quitMessage := <-session.panicChannel:
			panic(quitMessage)
		}
	}
	panic("Unexpected code path")
}

func (session *Session) GetPrompt() string {
	prompt := session.prompt
	prompt = strings.Replace(prompt, "%h", strconv.Itoa(session.player.GetHitPoints()), -1)
	prompt = strings.Replace(prompt, "%H", strconv.Itoa(session.player.GetHealth()), -1)

	return utils.Colorize(session.user.GetColorMode(), utils.ColorWhite, prompt)
}

// vim: nocindent
<|endoftext|>


<commit_before>// Package tgbotapi has bindings for interacting with the Telegram Bot API.
package tgbotapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/technoweenie/multipartstreamer"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

// BotAPI has methods for interacting with all of Telegram's Bot API endpoints.
type BotAPI struct {
	Token   string       `json:"token"`
	Debug   bool         `json:"debug"`
	Self    User         `json:"-"`
	Updates chan Update  `json:"-"`
	Client  *http.Client `json:"-"`
}

// NewBotAPI creates a new BotAPI instance.
// Requires a token, provided by @BotFather on Telegram
func NewBotAPI(token string) (*BotAPI, error) {
	return NewBotAPIWithClient(token, &http.Client{})
}

// NewBotAPIWithClient creates a new BotAPI instance passing an http.Client.
// Requires a token, provided by @BotFather on Telegram
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
	bot := &BotAPI{
		Token:  token,
		Client: client,
	}

	self, err := bot.GetMe()
	if err != nil {
		return &BotAPI{}, err
	}

	bot.Self = self

	return bot, nil
}

// MakeRequest makes a request to a specific endpoint with our token.
// All requests are POSTs because Telegram doesn't care, and it's easier.
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
	resp, err := bot.Client.PostForm(fmt.Sprintf(APIEndpoint, bot.Token, endpoint), params)
	if err != nil {
		return APIResponse{}, err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusForbidden {
		return APIResponse{}, errors.New(APIForbidden)
	}

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

	if bot.Debug {
		log.Println(endpoint, string(bytes))
	}

	var apiResp APIResponse
	json.Unmarshal(bytes, &apiResp)

	if !apiResp.Ok {
		return APIResponse{}, errors.New(apiResp.Description)
	}

	return apiResp, nil
}

func (bot *BotAPI) MakeMessageRequest(endpoint string, params url.Values) (Message, error) {
	resp, err := bot.MakeRequest(endpoint, params)
	if err != nil {
		return Message{}, err
	}

	var message Message
	json.Unmarshal(resp.Result, &message)

	bot.debugLog(endpoint, params, message)

	return message, nil
}

// UploadFile makes a request to the API with a file.
//
// Requires the parameter to hold the file not be in the params.
// File should be a string to a file path, a FileBytes struct, or a FileReader struct.
func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
	ms := multipartstreamer.New()
	ms.WriteFields(params)

	switch f := file.(type) {
	case string:
		fileHandle, err := os.Open(f)
		if err != nil {
			return APIResponse{}, err
		}
		defer fileHandle.Close()

		fi, err := os.Stat(f)
		if err != nil {
			return APIResponse{}, err
		}

		ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
	case FileBytes:
		buf := bytes.NewBuffer(f.Bytes)
		ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
	case FileReader:
		if f.Size == -1 {
			data, err := ioutil.ReadAll(f.Reader)
			if err != nil {
				return APIResponse{}, err
			}
			buf := bytes.NewBuffer(data)

			ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)

			break
		}

		ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
	default:
		return APIResponse{}, errors.New("bad file type")
	}

	req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), nil)
	ms.SetupRequest(req)
	if err != nil {
		return APIResponse{}, err
	}

	res, err := bot.Client.Do(req)
	if err != nil {
		return APIResponse{}, err
	}
	defer res.Body.Close()

	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return APIResponse{}, err
	}

	if bot.Debug {
		log.Println(string(bytes[:]))
	}

	var apiResp APIResponse
	json.Unmarshal(bytes, &apiResp)

	if !apiResp.Ok {
		return APIResponse{}, errors.New(apiResp.Description)
	}

	return apiResp, nil
}

func (this *BotAPI) getFileDirectUrl(fileID string) (string, error) {
	file, err := this.GetFile(FileConfig{fileID})

	if err != nil {
		return "", err
	}

	return file.Link(this.Token), nil
}

// GetMe fetches the currently authenticated bot.
//
// There are no parameters for this method.
func (bot *BotAPI) GetMe() (User, error) {
	resp, err := bot.MakeRequest("getMe", nil)
	if err != nil {
		return User{}, err
	}

	var user User
	json.Unmarshal(resp.Result, &user)

	if bot.Debug {
		log.Printf("getMe: %+v\n", user)
	}

	return user, nil
}

func (bot *BotAPI) Send(c Chattable) (Message, error) {
	switch c.(type) {
	case Fileable:
		return bot.sendFile(c.(Fileable))
	default:
		return bot.sendChattable(c)
	}
}

func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
	if bot.Debug {
		log.Printf("%s req : %+v\n", context, v)
		log.Printf("%s resp: %+v\n", context, message)
	}
}

func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
	v, err := config.Values()

	if err != nil {
		return Message{}, err
	}

	message, err := bot.MakeMessageRequest(method, v)
	if err != nil {
		return Message{}, err
	}

	return message, nil
}

func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
	params, err := config.Params()
	if err != nil {
		return Message{}, err
	}

	file := config.GetFile()

	resp, err := bot.UploadFile(method, params, config.Name(), file)
	if err != nil {
		return Message{}, err
	}

	var message Message
	json.Unmarshal(resp.Result, &message)

	if bot.Debug {
		log.Printf("%s resp: %+v\n", method, message)
	}

	return message, nil
}

func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
	if config.UseExistingFile() {
		return bot.sendExisting(config.Method(), config)
	}

	return bot.uploadAndSend(config.Method(), config)
}

func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
	v, err := config.Values()
	if err != nil {
		return Message{}, err
	}

	message, err := bot.MakeMessageRequest(config.Method(), v)

	if err != nil {
		return Message{}, err
	}

	return message, nil
}

// GetUserProfilePhotos gets a user's profile photos.
//
// Requires UserID.
// Offset and Limit are optional.
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
	v := url.Values{}
	v.Add("user_id", strconv.Itoa(config.UserID))
	if config.Offset != 0 {
		v.Add("offset", strconv.Itoa(config.Offset))
	}
	if config.Limit != 0 {
		v.Add("limit", strconv.Itoa(config.Limit))
	}

	resp, err := bot.MakeRequest("getUserProfilePhotos", v)
	if err != nil {
		return UserProfilePhotos{}, err
	}

	var profilePhotos UserProfilePhotos
	json.Unmarshal(resp.Result, &profilePhotos)

	bot.debugLog("GetUserProfilePhoto", v, profilePhotos)

	return profilePhotos, nil
}

// GetFile returns a file_id required to download a file.
//
// Requires FileID.
func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
	v := url.Values{}
	v.Add("file_id", config.FileID)

	resp, err := bot.MakeRequest("getFile", v)
	if err != nil {
		return File{}, err
	}

	var file File
	json.Unmarshal(resp.Result, &file)

	bot.debugLog("GetFile", v, file)

	return file, nil
}

// GetUpdates fetches updates.
// If a WebHook is set, this will not return any data!
//
// Offset, Limit, and Timeout are optional.
// To not get old items, set Offset to one higher than the previous item.
// Set Timeout to a large number to reduce requests and get responses instantly.
func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
	v := url.Values{}
	if config.Offset > 0 {
		v.Add("offset", strconv.Itoa(config.Offset))
	}
	if config.Limit > 0 {
		v.Add("limit", strconv.Itoa(config.Limit))
	}
	if config.Timeout > 0 {
		v.Add("timeout", strconv.Itoa(config.Timeout))
	}

	resp, err := bot.MakeRequest("getUpdates", v)
	if err != nil {
		return []Update{}, err
	}

	var updates []Update
	json.Unmarshal(resp.Result, &updates)

	if bot.Debug {
		log.Printf("getUpdates: %+v\n", updates)
	}

	return updates, nil
}

func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
	return bot.MakeRequest("setWebhook", url.Values{})
}

// SetWebhook sets a webhook.
// If this is set, GetUpdates will not get any data!
//
// Requires URL OR to set Clear to true.
func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
	if config.Certificate == nil {
		v := url.Values{}
		v.Add("url", config.URL.String())

		return bot.MakeRequest("setWebhook", v)
	}

	params := make(map[string]string)
	params["url"] = config.URL.String()

	resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
	if err != nil {
		return APIResponse{}, err
	}

	var apiResp APIResponse
	json.Unmarshal(resp.Result, &apiResp)

	if bot.Debug {
		log.Printf("setWebhook resp: %+v\n", apiResp)
	}

	return apiResp, nil
}

// UpdatesChan starts a channel for getting updates.
func (bot *BotAPI) UpdatesChan(config UpdateConfig) error {
	bot.Updates = make(chan Update, 100)

	go func() {
		for {
			updates, err := bot.GetUpdates(config)
			if err != nil {
				log.Println(err)
				log.Println("Failed to get updates, retrying in 3 seconds...")
				time.Sleep(time.Second * 3)

				continue
			}

			for _, update := range updates {
				if update.UpdateID >= config.Offset {
					config.Offset = update.UpdateID + 1
					bot.Updates <- update
				}
			}
		}
	}()

	return nil
}

// ListenForWebhook registers a http handler for a webhook.
func (bot *BotAPI) ListenForWebhook(pattern string) http.Handler {
	bot.Updates = make(chan Update, 100)

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		bytes, _ := ioutil.ReadAll(r.Body)

		var update Update
		json.Unmarshal(bytes, &update)

		bot.Updates <- update
	})

	http.HandleFunc(pattern, handler)

	return handler
}
<commit_msg>Typo fix<commit_after>// Package tgbotapi has bindings for interacting with the Telegram Bot API.
package tgbotapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/technoweenie/multipartstreamer"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

// BotAPI has methods for interacting with all of Telegram's Bot API endpoints.
type BotAPI struct {
	Token   string       `json:"token"`
	Debug   bool         `json:"debug"`
	Self    User         `json:"-"`
	Updates chan Update  `json:"-"`
	Client  *http.Client `json:"-"`
}

// NewBotAPI creates a new BotAPI instance.
// Requires a token, provided by @BotFather on Telegram
func NewBotAPI(token string) (*BotAPI, error) {
	return NewBotAPIWithClient(token, &http.Client{})
}

// NewBotAPIWithClient creates a new BotAPI instance passing an http.Client.
// Requires a token, provided by @BotFather on Telegram
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
	bot := &BotAPI{
		Token:  token,
		Client: client,
	}

	self, err := bot.GetMe()
	if err != nil {
		return &BotAPI{}, err
	}

	bot.Self = self

	return bot, nil
}

// MakeRequest makes a request to a specific endpoint with our token.
// All requests are POSTs because Telegram doesn't care, and it's easier.
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
	resp, err := bot.Client.PostForm(fmt.Sprintf(APIEndpoint, bot.Token, endpoint), params)
	if err != nil {
		return APIResponse{}, err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusForbidden {
		return APIResponse{}, errors.New(APIForbidden)
	}

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

	if bot.Debug {
		log.Println(endpoint, string(bytes))
	}

	var apiResp APIResponse
	json.Unmarshal(bytes, &apiResp)

	if !apiResp.Ok {
		return APIResponse{}, errors.New(apiResp.Description)
	}

	return apiResp, nil
}

func (bot *BotAPI) MakeMessageRequest(endpoint string, params url.Values) (Message, error) {
	resp, err := bot.MakeRequest(endpoint, params)
	if err != nil {
		return Message{}, err
	}

	var message Message
	json.Unmarshal(resp.Result, &message)

	bot.debugLog(endpoint, params, message)

	return message, nil
}

// UploadFile makes a request to the API with a file.
//
// Requires the parameter to hold the file not be in the params.
// File should be a string to a file path, a FileBytes struct, or a FileReader struct.
func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
	ms := multipartstreamer.New()
	ms.WriteFields(params)

	switch f := file.(type) {
	case string:
		fileHandle, err := os.Open(f)
		if err != nil {
			return APIResponse{}, err
		}
		defer fileHandle.Close()

		fi, err := os.Stat(f)
		if err != nil {
			return APIResponse{}, err
		}

		ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
	case FileBytes:
		buf := bytes.NewBuffer(f.Bytes)
		ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
	case FileReader:
		if f.Size == -1 {
			data, err := ioutil.ReadAll(f.Reader)
			if err != nil {
				return APIResponse{}, err
			}
			buf := bytes.NewBuffer(data)

			ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)

			break
		}

		ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
	default:
		return APIResponse{}, errors.New("bad file type")
	}

	req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), nil)
	ms.SetupRequest(req)
	if err != nil {
		return APIResponse{}, err
	}

	res, err := bot.Client.Do(req)
	if err != nil {
		return APIResponse{}, err
	}
	defer res.Body.Close()

	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return APIResponse{}, err
	}

	if bot.Debug {
		log.Println(string(bytes[:]))
	}

	var apiResp APIResponse
	json.Unmarshal(bytes, &apiResp)

	if !apiResp.Ok {
		return APIResponse{}, errors.New(apiResp.Description)
	}

	return apiResp, nil
}

func (this *BotAPI) GetFileDirectUrl(fileID string) (string, error) {
	file, err := this.GetFile(FileConfig{fileID})

	if err != nil {
		return "", err
	}

	return file.Link(this.Token), nil
}

// GetMe fetches the currently authenticated bot.
//
// There are no parameters for this method.
func (bot *BotAPI) GetMe() (User, error) {
	resp, err := bot.MakeRequest("getMe", nil)
	if err != nil {
		return User{}, err
	}

	var user User
	json.Unmarshal(resp.Result, &user)

	if bot.Debug {
		log.Printf("getMe: %+v\n", user)
	}

	return user, nil
}

func (bot *BotAPI) Send(c Chattable) (Message, error) {
	switch c.(type) {
	case Fileable:
		return bot.sendFile(c.(Fileable))
	default:
		return bot.sendChattable(c)
	}
}

func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
	if bot.Debug {
		log.Printf("%s req : %+v\n", context, v)
		log.Printf("%s resp: %+v\n", context, message)
	}
}

func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
	v, err := config.Values()

	if err != nil {
		return Message{}, err
	}

	message, err := bot.MakeMessageRequest(method, v)
	if err != nil {
		return Message{}, err
	}

	return message, nil
}

func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
	params, err := config.Params()
	if err != nil {
		return Message{}, err
	}

	file := config.GetFile()

	resp, err := bot.UploadFile(method, params, config.Name(), file)
	if err != nil {
		return Message{}, err
	}

	var message Message
	json.Unmarshal(resp.Result, &message)

	if bot.Debug {
		log.Printf("%s resp: %+v\n", method, message)
	}

	return message, nil
}

func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
	if config.UseExistingFile() {
		return bot.sendExisting(config.Method(), config)
	}

	return bot.uploadAndSend(config.Method(), config)
}

func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
	v, err := config.Values()
	if err != nil {
		return Message{}, err
	}

	message, err := bot.MakeMessageRequest(config.Method(), v)

	if err != nil {
		return Message{}, err
	}

	return message, nil
}

// GetUserProfilePhotos gets a user's profile photos.
//
// Requires UserID.
// Offset and Limit are optional.
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
	v := url.Values{}
	v.Add("user_id", strconv.Itoa(config.UserID))
	if config.Offset != 0 {
		v.Add("offset", strconv.Itoa(config.Offset))
	}
	if config.Limit != 0 {
		v.Add("limit", strconv.Itoa(config.Limit))
	}

	resp, err := bot.MakeRequest("getUserProfilePhotos", v)
	if err != nil {
		return UserProfilePhotos{}, err
	}

	var profilePhotos UserProfilePhotos
	json.Unmarshal(resp.Result, &profilePhotos)

	bot.debugLog("GetUserProfilePhoto", v, profilePhotos)

	return profilePhotos, nil
}

// GetFile returns a file_id required to download a file.
//
// Requires FileID.
func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
	v := url.Values{}
	v.Add("file_id", config.FileID)

	resp, err := bot.MakeRequest("getFile", v)
	if err != nil {
		return File{}, err
	}

	var file File
	json.Unmarshal(resp.Result, &file)

	bot.debugLog("GetFile", v, file)

	return file, nil
}

// GetUpdates fetches updates.
// If a WebHook is set, this will not return any data!
//
// Offset, Limit, and Timeout are optional.
// To not get old items, set Offset to one higher than the previous item.
// Set Timeout to a large number to reduce requests and get responses instantly.
func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
	v := url.Values{}
	if config.Offset > 0 {
		v.Add("offset", strconv.Itoa(config.Offset))
	}
	if config.Limit > 0 {
		v.Add("limit", strconv.Itoa(config.Limit))
	}
	if config.Timeout > 0 {
		v.Add("timeout", strconv.Itoa(config.Timeout))
	}

	resp, err := bot.MakeRequest("getUpdates", v)
	if err != nil {
		return []Update{}, err
	}

	var updates []Update
	json.Unmarshal(resp.Result, &updates)

	if bot.Debug {
		log.Printf("getUpdates: %+v\n", updates)
	}

	return updates, nil
}

func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
	return bot.MakeRequest("setWebhook", url.Values{})
}

// SetWebhook sets a webhook.
// If this is set, GetUpdates will not get any data!
//
// Requires URL OR to set Clear to true.
func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
	if config.Certificate == nil {
		v := url.Values{}
		v.Add("url", config.URL.String())

		return bot.MakeRequest("setWebhook", v)
	}

	params := make(map[string]string)
	params["url"] = config.URL.String()

	resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
	if err != nil {
		return APIResponse{}, err
	}

	var apiResp APIResponse
	json.Unmarshal(resp.Result, &apiResp)

	if bot.Debug {
		log.Printf("setWebhook resp: %+v\n", apiResp)
	}

	return apiResp, nil
}

// UpdatesChan starts a channel for getting updates.
func (bot *BotAPI) UpdatesChan(config UpdateConfig) error {
	bot.Updates = make(chan Update, 100)

	go func() {
		for {
			updates, err := bot.GetUpdates(config)
			if err != nil {
				log.Println(err)
				log.Println("Failed to get updates, retrying in 3 seconds...")
				time.Sleep(time.Second * 3)

				continue
			}

			for _, update := range updates {
				if update.UpdateID >= config.Offset {
					config.Offset = update.UpdateID + 1
					bot.Updates <- update
				}
			}
		}
	}()

	return nil
}

// ListenForWebhook registers a http handler for a webhook.
func (bot *BotAPI) ListenForWebhook(pattern string) http.Handler {
	bot.Updates = make(chan Update, 100)

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		bytes, _ := ioutil.ReadAll(r.Body)

		var update Update
		json.Unmarshal(bytes, &update)

		bot.Updates <- update
	})

	http.HandleFunc(pattern, handler)

	return handler
}
<|endoftext|>


<commit_before>// Get the number of open files for a process and compare that against /proc/<pid>/limits and alert if
// over the given threshold.
//
//
// LICENSE:
//   Copyright 2015 Yieldbot. <devops@yieldbot.com>
//   Released under the MIT License; see LICENSE
//   for details.

package main

import (
//	"encoding/json"
	"flag"
	"fmt"
	"github.com/yieldbot/dhuran"
//	"github.com/yieldbot/dracky"
//	"github.com/olivere/elastic"
	// "io/ioutil"
  // "github.com/shirou/gopsutil/process"
  "strconv"
  "regexp"
  "strings"
	"os"
  "os/exec"
//	"time"
)

// Get the pid for the supplied process
func get_pid(app string) string {
  pid_exp := regexp.MustCompile("[0-9]+")
  go_pid := os.Getpid()
  var a_pid string
  fmt.Printf("golang binary pid: %v\n", go_pid)

  ps_aef := exec.Command("ps", "-aef")

  out, err := ps_aef.Output()
  if err != nil {
    dhuran.Check(err)
  }

  ps_aef.Start()

  lines := strings.Split(string(out), "\n")
  for i := range lines {
    if (strings.Contains(lines[i], app) && ! strings.Contains(lines[i], strconv.Itoa(go_pid))){
      // fmt.Printf("%v\n", lines[i])
      a_pid = pid_exp.FindString(lines[i])
      return a_pid
      // matty := strings.Split(lines[i]," ")
    // for j := range matty {
    //   fmt.Printf("%v\n", matty[j])
    // }
    //     return lines[i]
    }
  }
  return ""
}

// Calculate if the value is over a threshold
// func determine_threshold(val int, threshold int) bool {
//
// }
//
// // Get the current number of open file handles for the process
// func get_file_handles(pid string) int {
//
// }

func main() {

	// set commandline flags
	AppPtr := flag.String("app", "sbin/init", "the process name")
	WarnPtr := flag.Int("warn", 75, "the alert warning threshold percentage")
	CritPtr := flag.Int("crit", 75, "the alert critical threshold percentage")

	flag.Parse()
	app := *AppPtr
	warn_threshold := *WarnPtr
	crit_threshold := *CritPtr

  var app_pid string

	if app != "" {
    // YELLOW check for a null or nil string
    // need to check for the process better, regex?
    app_pid = get_pid(app)

  } else {
    fmt.Printf("Please enter a process name to check")
		os.Exit(100)
    }

    fmt.Printf("warning threshold: %v, critical threshold: %v\n", warn_threshold, crit_threshold)
    fmt.Printf("app pid is: %v\n", app_pid)

	// 	if err != nil {
	// 		dhuran.Check(err)
	// 	}
	// 	err = json.Unmarshal(user_input, &user_event)
	// 	if err != nil {
	// 		dhuran.Check(err)
	// 	}
	// 	es_type = "user"
	// } else if (rd_stdin == false) && (input_file == "") {
	// 	fmt.Printf("Please enter a file to read from")
	// 	os.Exit(1)
	// } else {
	// 	sensu_event = sensu_event.Acquire_sensu_event()
	// }
  //
	// // Create a client
	// client, err := elastic.NewClient(
	// 	elastic.SetURL("http://" + es_host + ":" + es_port),
	// )
	// if err != nil {
	// 	dhuran.Check(err)
	// }
  //
	// // Check to see if the index exists and if not create it
	// if client.IndexExists == nil { // need to test to make sure this does what I want
	// 	_, err = client.CreateIndex(es_index).Do()
	// 	if err != nil {
	// 		dhuran.Check(err)
	// 	}
	// }
  //
	// // Create an Elasticsearch document. The document type will define the mapping used for the document.
	// doc := make(map[string]string)
	// var doc_id string
	// switch es_type {
	// case "sensu":
	// 	doc_id = dracky.Event_name(sensu_event.Client.Name, sensu_event.Check.Name)
	// 	doc["monitored_instance"] = sensu_event.Acquire_monitored_instance()
	// 	doc["sensu_client"] = sensu_event.Client.Name
	// 	doc["incident_timestamp"] = time.Unix(sensu_event.Check.Issued, 0).Format(time.RFC822Z)
	// 	doc["check_name"] = dracky.Create_check_name(sensu_event.Check.Name)
	// 	doc["check_state"] = dracky.Define_status(sensu_event.Check.Status)
	// 	doc["sensu_env"] = dracky.Define_sensu_env(sensu_env.Sensu.Environment)
	// 	doc["instance_address"] = sensu_event.Client.Address
	// 	doc["check_state_duration"] = dracky.Define_check_state_duration()
	// case "user":
	// 	doc["product"] = user_event.Product
	// 	doc["data"] = user_event.Data
	// 	doc["timestamp"] = time.Unix(sensu_event.Check.Issued, 0).Format(time.RFC822Z) // dracky.Set_time(user_event.Timestamp)
	// default:
	// 	fmt.Printf("Type is not correctly set")
	// 	os.Exit(2)
	// }
  //
	// // Add a document to the Elasticsearch index
	// _, err = client.Index().
	// 	Index(es_index).
	// 	Type(es_type).
	// 	Id(doc_id).
	// 	BodyJson(doc).
	// 	Do()
	// if err != nil {
	// 	dhuran.Check(err)
	// }
  //
	// // Log a successful document push to stdout. I don't add the id here as some id's are fixed but
	// // the user has the ability to autogenerate an id if they don't want to provide one.
	// fmt.Printf("Record added to ES\n")
}
<commit_msg>complete and working<commit_after>// Get the number of open files for a process and compare that against /proc/<pid>/limits and alert if
// over the given threshold.
//
//
// LICENSE:
//   Copyright 2015 Yieldbot. <devops@yieldbot.com>
//   Released under the MIT License; see LICENSE
//   for details.

package main

import (
	"flag"
	"fmt"
	"github.com/yieldbot/dhuran"
	"io/ioutil"
	// "github.com/shirou/gopsutil/process"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

// Get the pid for the supplied process
func get_pid(app string) string {
	pid_exp := regexp.MustCompile("[0-9]+")
	go_pid := os.Getpid()
	a_pid := ""
	fmt.Printf("golang binary pid: %v\n", go_pid)

	ps_aef := exec.Command("ps", "-aef")

	out, err := ps_aef.Output()
	if err != nil {
		dhuran.Check(err)
	}

	ps_aef.Start()

	lines := strings.Split(string(out), "\n")
	for i := range lines {
		if strings.Contains(lines[i], app) && !strings.Contains(lines[i], strconv.Itoa(go_pid)) {
			a_pid = pid_exp.FindString(lines[i])
      break
		}
	}
	return a_pid
}

// Calculate if the value is over a threshold
func determine_threshold(limit int, threshold int, num_fd int) bool {
  alarm := true
  t_limit := (float64(threshold) / float64(100)) * float64(limit)

  if float64(num_fd) > float64(t_limit) {
    alarm = true
  } else {
    alarm = false
  }
  return alarm
}

// Get the current number of open file handles for the process
func get_file_handles(pid string) (int, int, int) {
	var _s, _h string
	var s, h int
	limit_exp := regexp.MustCompile("[0-9]+")
	filename := `/proc/` + pid + `/limits`
  fd_loc := "/proc/" + pid + "/fd"
  num_fd := 0

	limits, err := ioutil.ReadFile(filename)
	if err != nil {
		dhuran.Check(err)
	}

	lines := strings.Split(string(limits), "\n")
	for i := range lines {
		if strings.Contains(lines[i], "open files") {
			limits := limit_exp.FindAllString(lines[i], 2)
			_s = limits[0]
			_h = limits[1]

			s, err = strconv.Atoi(_s)
			if err != nil {
				// YELLOW handle error
				fmt.Println(err)
				os.Exit(2)
			}
			h, err = strconv.Atoi(_h)
			if err != nil {
				// YELLOW handle error
				fmt.Println(err)
				os.Exit(2)
			}
		}
	}

  files, _ := ioutil.ReadDir(fd_loc)
    for _ = range files {
      num_fd = num_fd + 1
    }
	return s, h, num_fd
}

func main() {

	// set commandline flags
	AppPtr := flag.String("app", "sbin/init", "the process name")
	WarnPtr := flag.Int("warn", 75, "the alert warning threshold percentage")
	CritPtr := flag.Int("crit", 75, "the alert critical threshold percentage")

	flag.Parse()
	app := *AppPtr
	warn_threshold := *WarnPtr
	crit_threshold := *CritPtr

	var app_pid string
	var s_limit, h_limit, open_fd int

	if app != "" {
		// YELLOW check for a null or nil string
		// need to check for the process better, regex?
		app_pid = get_pid(app)
		s_limit, h_limit , open_fd = get_file_handles(app_pid)
    if determine_threshold(h_limit, crit_threshold, open_fd) {
      fmt.Printf("%v is over %v percent of the the open file handles hard limit of %v\n", app, crit_threshold, h_limit)
      os.Exit(2)
    } else if determine_threshold(s_limit, warn_threshold, open_fd) {
        fmt.Printf("%v is over %v percent of the open file handles soft limit of %v\n", app, warn_threshold, s_limit)
        os.Exit(1)
    } else {
      // YELLOW need to set some other conditions here in case this fails
      fmt.Printf("warning threshold: %v percent, critical threshold: %v percent\n", warn_threshold, crit_threshold)
      fmt.Printf("this is the number of open files at the specific point in time: %v\n", open_fd)
    	fmt.Printf("app pid is: %v\n", app_pid)
    	fmt.Printf("This is the soft limit: %v\n", s_limit)
    	fmt.Printf("This is the hard limit: %v\n", h_limit)
      os.Exit(0)
    }
	} else {
		fmt.Printf("Please enter a process name to check")
		os.Exit(100)
	}
}
<|endoftext|>


<commit_before>// +build e2e

/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package conformance

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"testing"
	"time"

	"golang.org/x/sync/errgroup"

	_ "github.com/knative/pkg/system/testing"
	pkgTest "github.com/knative/pkg/test"
	"github.com/knative/serving/test"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestSingleConcurrency(t *testing.T) {
	t.Parallel()
	clients := setup(t)

	svcName := test.ObjectNameForTest(t)
	names := test.ResourceNames{
		Config: svcName,
		Route:  svcName,
		Image:  singleThreadedImage,
	}

	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
	defer test.TearDown(clients, names)

	configOptions := test.Options{
		ContainerConcurrency: 1,
	}
	t.Log("Creating a new Configuration")
	if _, err := test.CreateConfiguration(t, clients, names, &configOptions); err != nil {
		t.Fatalf("Failed to create Configuration: %v", err)
	}

	t.Log("Creating a new Route")
	if _, err := test.CreateRoute(t, clients, names); err != nil {
		t.Fatalf("Failed to create Route: %v", err)
	}

	t.Log("The Configuration will be updated with the name of the Revision")
	var err error
	names.Revision, err = test.WaitForConfigLatestRevision(clients, names)
	if err != nil {
		t.Fatalf("Configuration %s was not updated with the new revision: %v", names.Config, err)
	}

	t.Log("When the Route reports as Ready, everything should be ready.")
	if err := test.WaitForRouteState(clients.ServingClient, names.Route, test.IsRouteReady, "RouteIsReady"); err != nil {
		t.Fatalf("The Route %s was not marked as Ready to serve traffic: %v", names.Route, err)
	}

	route, err := clients.ServingClient.Routes.Get(names.Route, metav1.GetOptions{})
	if err != nil {
		t.Fatalf("Error fetching Route %s: %v", names.Route, err)
	}
	domain := route.Status.Domain

	// Ready does not actually mean Ready for a Route just yet.
	// See https://github.com/knative/serving/issues/1582
	t.Logf("Probing domain %s", domain)
	if _, err := pkgTest.WaitForEndpointState(
		clients.KubeClient,
		t.Logf,
		domain,
		test.RetryingRouteInconsistency(pkgTest.IsStatusOK),
		"WaitForSuccessfulResponse",
		test.ServingFlags.ResolvableDomain); err != nil {
		t.Fatalf("Error probing domain %s: %v", domain, err)
	}

	client, err := pkgTest.NewSpoofingClient(clients.KubeClient, t.Logf, domain, test.ServingFlags.ResolvableDomain)
	if err != nil {
		t.Fatalf("Error creating spoofing client: %v", err)
	}

	concurrency := 5
	duration := 20 * time.Second
	t.Logf("Maintaining %d concurrent requests for %v.", concurrency, duration)
	group, _ := errgroup.WithContext(context.Background())
	for i := 0; i < concurrency; i++ {
		group.Go(func() error {
			done := time.After(duration)
			req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s", domain), nil)
			if err != nil {
				return fmt.Errorf("error creating http request: %v", err)
			}

			for {
				select {
				case <-done:
					return nil
				default:
					res, err := client.Do(req)
					if err != nil {
						return fmt.Errorf("error making request %v", err)
					}
					if res.StatusCode == http.StatusInternalServerError {
						return errors.New("detected concurrent requests")
					} else if res.StatusCode != http.StatusOK {
						return fmt.Errorf("non 200 response %v", res.StatusCode)
					}
				}
			}
		})
	}
	t.Log("Waiting for all requests to complete.")
	if err := group.Wait(); err != nil {
		t.Fatalf("Error making requests for single threaded test: %v.", err)
	}
}
<commit_msg>Convert TestSingleConcurrency to use Service. (#3285)<commit_after>// +build e2e

/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package conformance

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"testing"
	"time"

	"golang.org/x/sync/errgroup"

	_ "github.com/knative/pkg/system/testing"
	pkgTest "github.com/knative/pkg/test"
	"github.com/knative/serving/test"
)

func TestSingleConcurrency(t *testing.T) {
	t.Parallel()
	clients := setup(t)

	names := test.ResourceNames{
		Service: test.ObjectNameForTest(t),
		Image:   singleThreadedImage,
	}
	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
	defer test.TearDown(clients, names)

	objects, err := test.CreateRunLatestServiceReady(t, clients, &names, &test.Options{
		ContainerConcurrency: 1,
	})
	if err != nil {
		t.Fatalf("Failed to create Service: %v", err)
	}
	domain := objects.Service.Status.Domain

	// Ready does not actually mean Ready for a Route just yet.
	// See https://github.com/knative/serving/issues/1582
	t.Logf("Probing domain %s", domain)
	if _, err := pkgTest.WaitForEndpointState(
		clients.KubeClient,
		t.Logf,
		domain,
		test.RetryingRouteInconsistency(pkgTest.IsStatusOK),
		"WaitForSuccessfulResponse",
		test.ServingFlags.ResolvableDomain); err != nil {
		t.Fatalf("Error probing domain %s: %v", domain, err)
	}

	client, err := pkgTest.NewSpoofingClient(clients.KubeClient, t.Logf, domain, test.ServingFlags.ResolvableDomain)
	if err != nil {
		t.Fatalf("Error creating spoofing client: %v", err)
	}

	concurrency := 5
	duration := 20 * time.Second
	t.Logf("Maintaining %d concurrent requests for %v.", concurrency, duration)
	group, _ := errgroup.WithContext(context.Background())
	for i := 0; i < concurrency; i++ {
		group.Go(func() error {
			done := time.After(duration)
			req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s", domain), nil)
			if err != nil {
				return fmt.Errorf("error creating http request: %v", err)
			}

			for {
				select {
				case <-done:
					return nil
				default:
					res, err := client.Do(req)
					if err != nil {
						return fmt.Errorf("error making request %v", err)
					}
					if res.StatusCode == http.StatusInternalServerError {
						return errors.New("detected concurrent requests")
					} else if res.StatusCode != http.StatusOK {
						return fmt.Errorf("non 200 response %v", res.StatusCode)
					}
				}
			}
		})
	}
	t.Log("Waiting for all requests to complete.")
	if err := group.Wait(); err != nil {
		t.Fatalf("Error making requests for single threaded test: %v.", err)
	}
}
<|endoftext|>


<commit_before>package pkg

import (
	"errors"
	"log"
	"net"
	"net/http"
	"net/rpc"
	"reflect"

	"github.com/avabot/ava/shared/datatypes"
)

// Pkg holds config options for any Ava package. Name must be globally unique
// Port takes the format of ":1234". Note that the colon is significant.
// ServerAddress will default to localhost if left blank.
type Pkg struct {
	Config  PkgConfig
	Trigger *datatypes.StructuredInput
}

type PkgConfig struct {
	Name          string
	Port          string
	ServerAddress string
}

type Ava int

var client *rpc.Client
var (
	ErrMissingPackageName = errors.New("missing package name")
	ErrMissingPort        = errors.New("missing package port")
	ErrMissingTrigger     = errors.New("missing package trigger")
)

func NewPackage(name, port, serverAddr string,
	trigger *datatypes.StructuredInput) (*Pkg, error) {

	if len(name) == 0 {
		return &Pkg{}, ErrMissingPackageName
	}
	if len(port) == 0 {
		return &Pkg{}, ErrMissingPort
	}
	if reflect.DeepEqual(trigger, datatypes.StructuredInput{}) {
		return &Pkg{}, ErrMissingTrigger
	}
	c := PkgConfig{
		Name:          name,
		Port:          port,
		ServerAddress: serverAddr,
	}
	return &Pkg{Config: c, Trigger: trigger}, nil
}

// Register with Ava to begin communicating over RPC.
func (p *Pkg) Register() error {
	var err error
	client, err = rpc.DialHTTP("tcp", p.Config.ServerAddress+":4001")
	if err != nil {
		return err
	}
	var replyErr error
	err = client.Call("Ava:RegisterPackage", p, &replyErr)
	if err != nil {
		return err
	}
	if replyErr != nil {
		return replyErr
	}
	bootRPCServer(p.Config.Port)
	return nil
}

func bootRPCServer(port string) {
	ava := new(Ava)
	rpc.Register(ava)
	rpc.HandleHTTP()
	l, e := net.Listen("tcp", port)
	if e != nil {
		log.Fatal("listen error:", e)
	}
	go http.Serve(l, nil)
}
<commit_msg>Fix nil check for NewPackage trigger<commit_after>package pkg

import (
	"errors"
	"log"
	"net"
	"net/http"
	"net/rpc"

	"github.com/avabot/ava/shared/datatypes"
)

// Pkg holds config options for any Ava package. Name must be globally unique
// Port takes the format of ":1234". Note that the colon is significant.
// ServerAddress will default to localhost if left blank.
type Pkg struct {
	Config  PkgConfig
	Trigger *datatypes.StructuredInput
}

type PkgConfig struct {
	Name          string
	Port          string
	ServerAddress string
}

type Ava int

var client *rpc.Client
var (
	ErrMissingPackageName = errors.New("missing package name")
	ErrMissingPort        = errors.New("missing package port")
	ErrMissingTrigger     = errors.New("missing package trigger")
)

func NewPackage(name, port, serverAddr string,
	trigger *datatypes.StructuredInput) (*Pkg, error) {

	if len(name) == 0 {
		return &Pkg{}, ErrMissingPackageName
	}
	if len(port) == 0 {
		return &Pkg{}, ErrMissingPort
	}
	if trigger == nil {
		return &Pkg{}, ErrMissingTrigger
	}
	c := PkgConfig{
		Name:          name,
		Port:          port,
		ServerAddress: serverAddr,
	}
	return &Pkg{Config: c, Trigger: trigger}, nil
}

// Register with Ava to begin communicating over RPC.
func (p *Pkg) Register() error {
	var err error
	client, err = rpc.DialHTTP("tcp", p.Config.ServerAddress+":4001")
	if err != nil {
		return err
	}
	var replyErr error
	err = client.Call("Ava:RegisterPackage", p, &replyErr)
	if err != nil {
		return err
	}
	if replyErr != nil {
		return replyErr
	}
	bootRPCServer(p.Config.Port)
	return nil
}

func bootRPCServer(port string) {
	ava := new(Ava)
	rpc.Register(ava)
	rpc.HandleHTTP()
	l, e := net.Listen("tcp", port)
	if e != nil {
		log.Fatal("listen error:", e)
	}
	go http.Serve(l, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"

	"github.com/codegangsta/cli"
)

func registerJobs(app cli.App) *cli.App {
	app.Commands = append(app.Commands,
		[]cli.Command{
			{
				Name:  `jobs`,
				Usage: `SUBCOMMANDS for job information`,
				Subcommands: []cli.Command{
					{
						Name:   `list`,
						Usage:  `List outstanding jobs`,
						Action: runtime(cmdJobList),
					},
					{
						Name:   `show`,
						Usage:  `Show details about a job`,
						Action: runtime(cmdJobShow),
					},
					{
						Name:  `local`,
						Usage: `SUBCOMMANDS for locally saved jobs`,
						Subcommands: []cli.Command{
							{
								Name:   `outstanding`,
								Usage:  `List outstanding locally saved Jobs`,
								Action: runtime(cmdJobLocalOutstanding),
							},
							{
								Name:   `update`,
								Usage:  `Check and update status of oustanding locally cached jobs`,
								Action: runtime(cmdJobLocalUpdate),
							},
							{
								Name:   `list`,
								Usage:  `List all locally cached jobs`,
								Action: runtime(cmdJobLocalList),
							},
							{
								Name:   `prune`,
								Usage:  `Delete completed jobs from local cache`,
								Action: runtime(cmdJobLocalPrune),
							},
						},
					},
				},
			},
		}...,
	)
	return &app
}

func cmdJobList(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)
	if resp, err := adm.GetReq(`/jobs/`); err != nil {
		return err
	} else {
		fmt.Println(resp)
	}
	return nil
}

func cmdJobShow(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)
	if !utl.IsUUID(c.Args().First()) {
		return fmt.Errorf("Argument is not a UUID: %s", c.Args().First())
	}
	path := fmt.Sprintf("/jobs/%s", c.Args().First())
	if resp, err := adm.GetReq(path); err != nil {
		return err
	} else {
		fmt.Println(resp)
	}
	return nil
}

func cmdJobLocalOutstanding(c *cli.Context) error {
	jobs, err := store.GetActiveJobs()
	if err != nil {
		return err
	}

	if enc, err := json.Marshal(&jobs); err != nil {
		return err
	} else {
		fmt.Println(string(enc))
	}
	return nil
}

func cmdJobLocalUpdate(c *cli.Context) error {
	return nil
}

func cmdJobLocalList(c *cli.Context) error {
	return nil
}

func cmdJobLocalPrune(c *cli.Context) error {
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Jobs/outstanding: output as proto.Jobs JSON<commit_after>package main

import (
	"encoding/json"
	"fmt"

	"github.com/codegangsta/cli"
)

func registerJobs(app cli.App) *cli.App {
	app.Commands = append(app.Commands,
		[]cli.Command{
			{
				Name:  `jobs`,
				Usage: `SUBCOMMANDS for job information`,
				Subcommands: []cli.Command{
					{
						Name:   `list`,
						Usage:  `List outstanding jobs`,
						Action: runtime(cmdJobList),
					},
					{
						Name:   `show`,
						Usage:  `Show details about a job`,
						Action: runtime(cmdJobShow),
					},
					{
						Name:  `local`,
						Usage: `SUBCOMMANDS for locally saved jobs`,
						Subcommands: []cli.Command{
							{
								Name:   `outstanding`,
								Usage:  `List outstanding locally saved Jobs`,
								Action: runtime(cmdJobLocalOutstanding),
							},
							{
								Name:   `update`,
								Usage:  `Check and update status of oustanding locally cached jobs`,
								Action: runtime(cmdJobLocalUpdate),
							},
							{
								Name:   `list`,
								Usage:  `List all locally cached jobs`,
								Action: runtime(cmdJobLocalList),
							},
							{
								Name:   `prune`,
								Usage:  `Delete completed jobs from local cache`,
								Action: runtime(cmdJobLocalPrune),
							},
						},
					},
				},
			},
		}...,
	)
	return &app
}

func cmdJobList(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)
	if resp, err := adm.GetReq(`/jobs/`); err != nil {
		return err
	} else {
		fmt.Println(resp)
	}
	return nil
}

func cmdJobShow(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)
	if !utl.IsUUID(c.Args().First()) {
		return fmt.Errorf("Argument is not a UUID: %s", c.Args().First())
	}
	path := fmt.Sprintf("/jobs/%s", c.Args().First())
	if resp, err := adm.GetReq(path); err != nil {
		return err
	} else {
		fmt.Println(resp)
	}
	return nil
}

func cmdJobLocalOutstanding(c *cli.Context) error {
	jobs, err := store.GetActiveJobs()
	if err != nil {
		return err
	}

	pj := []proto.Job{}
	for _, iArray := range jobs {
		pj = append(pj, proto.Job{
			Id:       iArray[1],
			TsQueued: iArray[2],
			Type:     iArray[3],
		})
	}

	if enc, err := json.Marshal(&pj); err != nil {
		return err
	} else {
		fmt.Println(string(enc))
	}
	return nil
}

func cmdJobLocalUpdate(c *cli.Context) error {
	return nil
}

func cmdJobLocalList(c *cli.Context) error {
	return nil
}

func cmdJobLocalPrune(c *cli.Context) error {
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package start

import (
	"fmt"
	"os"
	"testing"

	flag "github.com/ogier/pflag"
	. "github.com/smartystreets/goconvey/convey"
)

func TestAdd(t *testing.T) {
	Convey("When adding a command to the command map, then...", t, func() {
		Commands.Add(&Command{
			Name: "cmd1",
		})

		Convey("the command map should contain it", func() {
			So(Commands["cmd1"], ShouldNotBeNil)
			So(Commands["cmd1"].Name, ShouldEqual, "cmd1")

		})

		Convey("adding a command with the same name should fail", func() {
			err := Commands.Add(&Command{
				Name: "cmd1",
			})
			So(err, ShouldNotBeNil)
		})

		Convey("adding a subcommand to the command should work", func() {
			err := Commands.Add(&Command{
				Parent: "cmd1",
				Name:   "subcmd1",
			})
			So(err, ShouldBeNil)
			So(len(Commands["cmd1"].children), ShouldEqual, 1)
			So(Commands["cmd1"].children["subcmd1"], ShouldNotBeNil)
			So(Commands["cmd1"].children["subcmd1"].Name, ShouldEqual, "subcmd1")

			Convey("adding the same subcommand twice should fail", func() {
				err := Commands.Add(&Command{
					Parent: "cmd1",
					Name:   "subcmd1",
				})
				So(err, ShouldNotBeNil)
			})

		})

		Reset(func() {
			Commands = make(CommandMap)
		})
	})

}

func TestCommands(t *testing.T) {

	var yes bool
	var size int

	// ContinueOnError is required when running goconvey as server; otherwise, unrecognized
	// flags that are passed to the test executable will cause an error:
	// "unknown shorthand flag: 't' in -test.v=true"
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)

	flag.BoolVarP(&yes, "yes", "y", false, "A boolean flag")
	flag.IntVarP(&size, "size", "s", 23, "An int flag")

	Commands = make(CommandMap)

	Convey("The flags should exist", t, func() {
		Parse()
		So(flag.Lookup("yes").Name, ShouldEqual, "yes")
		So(flag.Lookup("size").Name, ShouldEqual, "size")
	})

	Convey("When setting up some commands, then...", t, func() {
		Description = "This is the test application for the start package."

		Add(&Command{
			Name:  "test",
			Short: "A test command",
			Long:  "Command test helps testing the start package. It accepts all flags.",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the test command.")
				return nil
			},
		})

		Add(&Command{
			Name:  "flags",
			Flags: []string{"yes", "size"},
			Short: "A test command with flags",
			Long:  "Command flags helps testing flags.",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the testflags command.")
				fmt.Println("--yes is %v", yes)
				fmt.Println("--size is %v", size)
				return nil
			},
		})

		Add(&Command{
			Name:  "do",
			Short: "A command with subcommands",
			Long: `Command do helps testing subcommands.
Usage:
	do something
	do nothing`,
		})

		Add(&Command{
			Parent: "do",
			Name:   "something",
			Short:  "A subcommand that does something.",
			Long:   "do something does something",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the do something command.")
				return nil
			},
		})

		Add(&Command{
			Parent: "do",
			Name:   "nothing",
			Short:  "A subcommand that does nothing.",
			Long:   "do nothing does nothing",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the do nothing command.")
				return nil
			},
		})

		Parse()

		Convey("readCommand should identify all of them correctly", func() {
			cmd, err := readCommand([]string{"test", "arg1", "arg2"})
			So(cmd.Name, ShouldEqual, "test")
			So(cmd.Args, ShouldResemble, []string{"arg1", "arg2"})
			So(err, ShouldBeNil)

			cmd, err = readCommand([]string{"do", "something", "arg1"})
			So(cmd.Name, ShouldEqual, "something")
			So(cmd.Args, ShouldResemble, []string{"arg1"})
			So(err, ShouldBeNil)

			cmd, err = readCommand([]string{"do", "nothing"})
			So(cmd.Name, ShouldEqual, "nothing")
			So(cmd.Args, ShouldResemble, []string{})
			So(err, ShouldBeNil)
		})

		Convey("readCommand should return the Usage command if no valid command was passed in", func() {
			cmd, err := readCommand([]string{"invalid", "arg1"})
			So(cmd.Cmd, ShouldEqual, Usage)
			So(err, ShouldBeNil)
		})

		Convey("Usage() should print the usage", func() {
			Usage(nil)
			Usage(Commands["test"])
			Usage(Commands["flags"])
			Usage(Commands["do"])
			Usage(Commands["do"].children["something"])
			Usage(Commands["do"].children["nothing"])

		})
		Reset(func() {
			Commands = make(CommandMap)
		})
	})

}

func TestCheckFlags(t *testing.T) {

	SkipConvey("Given two commands with overlapping flag sets", t, func() {
		Convey("Each should accept its own flags", func() {
			// TODO
		})
		Convey("Each should reject the flags that belong to the other command only", func() {
			// TODO
		})
	})
}
<commit_msg>Added: Dummy flag vars for suppressing wrong warnings Implemented: TestCheckFlags<commit_after>package start

import (
	"fmt"
	"os"
	"testing"

	flag "github.com/ogier/pflag"
	. "github.com/smartystreets/goconvey/convey"
)

func TestAdd(t *testing.T) {
	Convey("When adding a command to the command map, then...", t, func() {
		Commands.Add(&Command{
			Name: "cmd1",
		})

		Convey("the command map should contain it", func() {
			So(Commands["cmd1"], ShouldNotBeNil)
			So(Commands["cmd1"].Name, ShouldEqual, "cmd1")

		})

		Convey("adding a command with the same name should fail", func() {
			err := Commands.Add(&Command{
				Name: "cmd1",
			})
			So(err, ShouldNotBeNil)
		})

		Convey("adding a subcommand to the command should work", func() {
			err := Commands.Add(&Command{
				Parent: "cmd1",
				Name:   "subcmd1",
			})
			So(err, ShouldBeNil)
			So(len(Commands["cmd1"].children), ShouldEqual, 1)
			So(Commands["cmd1"].children["subcmd1"], ShouldNotBeNil)
			So(Commands["cmd1"].children["subcmd1"].Name, ShouldEqual, "subcmd1")

			Convey("adding the same subcommand twice should fail", func() {
				err := Commands.Add(&Command{
					Parent: "cmd1",
					Name:   "subcmd1",
				})
				So(err, ShouldNotBeNil)
			})

		})

		Reset(func() {
			Commands = make(CommandMap)
		})
	})

}

func TestCommands(t *testing.T) {

	var yes bool
	var size int

	// ContinueOnError is required when running goconvey as server; otherwise, unrecognized
	// flags that are passed to the test executable will cause an error:
	// "unknown shorthand flag: 't' in -test.v=true"
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	// To suppress warnings resulting from standard flags -test and -json,
	// read -t and -j into dummy flags.
	// The flags used for actual testing must not use -t or -j shorthands.
	var testflag string
	var jsonflag string
	flag.StringVarP(&testflag, "t", "t", "t", "t")
	flag.StringVarP(&jsonflag, "j", "j", "j", "j")

	flag.BoolVarP(&yes, "yes", "y", false, "A boolean flag")
	flag.IntVarP(&size, "size", "s", 23, "An int flag")

	Commands = make(CommandMap) // Ensure the Commands map starts empty for the test

	os.Args = []string{os.Args[0]}

	Convey("Ensure that the test flags exist", t, func() {
		Parse()
		So(flag.Lookup("yes").Name, ShouldEqual, "yes")
		So(flag.Lookup("size").Name, ShouldEqual, "size")
	})

	Convey("When setting up some commands, then...", t, func() {
		Description = "This is the test application for the start package."

		Add(&Command{
			Name:  "test",
			Short: "A test command",
			Long:  "Command test helps testing the start package. It accepts all flags.",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the test command.")
				return nil
			},
		})

		Add(&Command{
			Name:  "flags",
			Flags: []string{"yes", "size"},
			Short: "A test command with flags",
			Long:  "Command flags helps testing flags.",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the testflags command.")
				fmt.Printf("--yes is %v", yes)
				fmt.Printf("--size is %v", size)
				return nil
			},
		})

		Add(&Command{
			Name:  "do",
			Short: "A command with subcommands",
			Long: `Command do helps testing subcommands.
Usage:
	do something
	do nothing`,
		})

		Add(&Command{
			Parent: "do",
			Name:   "something",
			Short:  "A subcommand that does something.",
			Long:   "do something does something",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the do something command.")
				return nil
			},
		})

		Add(&Command{
			Parent: "do",
			Name:   "nothing",
			Short:  "A subcommand that does nothing.",
			Long:   "do nothing does nothing",
			Cmd: func(cmd *Command) error {
				fmt.Println("This is the do nothing command.")
				return nil
			},
		})

		Parse()

		Convey("readCommand should identify all of them correctly", func() {
			cmd, err := readCommand([]string{"test", "arg1", "arg2"})
			So(cmd, ShouldNotBeNil)
			So(cmd.Name, ShouldEqual, "test")
			So(cmd.Args, ShouldResemble, []string{"arg1", "arg2"})
			So(err, ShouldBeNil)

			cmd, err = readCommand([]string{"do", "something", "arg1"})
			So(cmd, ShouldNotBeNil)
			So(cmd.Name, ShouldEqual, "something")
			So(cmd.Args, ShouldResemble, []string{"arg1"})
			So(err, ShouldBeNil)

			cmd, err = readCommand([]string{"do", "nothing"})
			So(cmd, ShouldNotBeNil)
			So(cmd.Name, ShouldEqual, "nothing")
			So(cmd.Args, ShouldResemble, []string{})
			So(err, ShouldBeNil)
		})

		Convey("readCommand should return the Usage command if no valid command was passed in", func() {
			cmd, err := readCommand([]string{"invalid", "arg1"})
			So(cmd, ShouldNotBeNil)
			So(cmd.Cmd, ShouldEqual, Usage)
			So(err, ShouldBeNil)
		})

		Convey("Usage() should print the usage", func() {
			Usage(nil)
			Usage(Commands["test"])
			Usage(Commands["flags"])
			Usage(Commands["do"])
			Usage(Commands["do"].children["something"])
			Usage(Commands["do"].children["nothing"])

		})
		Reset(func() {
			Commands = make(CommandMap)
		})
	})

}

func TestCheckFlags(t *testing.T) {
	var first int
	var second int
	var third int
	var global int

	// ContinueOnError is required when running goconvey as server; otherwise, unrecognized
	// flags that are passed to the test executable will cause an error:
	// "unknown shorthand flag: 't' in -test.v=true"
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	// To suppress warnings resulting from standard flags -test and -json,
	// read -t and -j into dummy flags.
	// The flags used for actual testing must not use -t or -j shorthands.
	var testflag string
	var jsonflag string
	flag.StringVarP(&testflag, "t", "t", "t", "t")
	flag.StringVarP(&jsonflag, "j", "j", "j", "j")

	flag.IntVarP(&first, "first", "f", 1, "The first flag")
	flag.IntVarP(&second, "second", "s", 2, "The second flag")
	flag.IntVarP(&third, "third", "h", 3, "The third flag")
	flag.IntVarP(&global, "global", "g", 4, "The global flag")

	os.Args = []string{os.Args[0], "--first=10", "--second=20", "--third=30", "--global=40", "anargument", "anotherarg"}

	Commands = make(CommandMap) // clear the commands map for this test
	Add(&Command{
		Name:  "cmd12",
		Flags: []string{"first", "second"},
	})
	Add(&Command{
		Name:  "cmd23",
		Flags: []string{"second", "third"},
	})
	Add(&Command{
		Name:  "cmd123",
		Flags: []string{"first", "second", "third"},
	})

	Parse()

	Convey("A command should accept its own flags and all global flags", t, func() {
		rejectedFlags := checkFlags(Commands["cmd123"])
		So(len(rejectedFlags), ShouldEqual, 0)
	})
	Convey("A command should reject the flags that belong to the other command only", t, func() {
		rejectedFlags := checkFlags(Commands["cmd23"])
		So(len(rejectedFlags), ShouldEqual, 1)
		So(rejectedFlags[0], ShouldEqual, "first")

		rejectedFlags = checkFlags(Commands["cmd12"])
		So(len(rejectedFlags), ShouldEqual, 1)
		So(rejectedFlags[0], ShouldEqual, "third")
	})
}
<|endoftext|>


<commit_before>package victoriametrics

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/ansel1/merry"
	"github.com/go-graphite/carbonapi/zipper/protocols/prometheus/helpers"
	prometheusTypes "github.com/go-graphite/carbonapi/zipper/protocols/prometheus/types"
	"github.com/go-graphite/carbonapi/zipper/types"
	protov3 "github.com/go-graphite/protocol/carbonapi_v3_pb"
	"go.uber.org/zap"
)

func (c *VictoriaMetricsGroup) Fetch(ctx context.Context, request *protov3.MultiFetchRequest) (*protov3.MultiFetchResponse, *types.Stats, merry.Error) {
	supportedFeatures, _ := c.featureSet.Load().(*vmSupportedFeatures)
	if !supportedFeatures.SupportOptimizedGraphiteFetch {
		// VictoriaMetrics <1.53.1 doesn't support graphite find api, reverting back to prometheus code-path
		return c.BackendServer.Fetch(ctx, request)
	}
	logger := c.logger.With(zap.String("type", "fetch"), zap.String("request", request.String()))
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/api/v1/query_range")

	pathExprToTargets := make(map[string][]string)
	for _, m := range request.Metrics {
		targets := pathExprToTargets[m.PathExpression]
		pathExprToTargets[m.PathExpression] = append(targets, m.Name)
	}

	var r protov3.MultiFetchResponse
	var e merry.Error

	start := request.Metrics[0].StartTime
	stop := request.Metrics[0].StopTime

	maxPointsPerQuery := c.maxPointsPerQuery
	if request.Metrics != nil && request.Metrics[0].MaxDataPoints != 0 {
		maxPointsPerQuery = request.Metrics[0].MaxDataPoints
	}
	step := helpers.AdjustStep(start, stop, maxPointsPerQuery, c.step)

	stepStr := strconv.FormatInt(step, 10)
	for pathExpr, targets := range pathExprToTargets {
		for _, target := range targets {
			logger.Debug("got some target to query",
				zap.Any("pathExpr", pathExpr),
				zap.Any("target", target),
			)
			// rewrite metric for Tag
			// Make local copy
			stepLocalStr := stepStr
			if strings.HasPrefix(target, "seriesByTag") {
				stepLocalStr, target = helpers.SeriesByTagToPromQL(stepLocalStr, target)
			} else {
				target = fmt.Sprintf("{__graphite__=%q}", target)
			}
			if stepLocalStr[len(stepLocalStr)-1] >= '0' && stepLocalStr[len(stepLocalStr)-1] <= '9' {
				stepLocalStr += "s"
			}
			t, err := time.ParseDuration(stepLocalStr)
			if err != nil {
				stats.RenderErrors += 1
				logger.Debug("failed to parse step",
					zap.String("step", stepLocalStr),
					zap.Error(err),
				)
				if e == nil {
					e = merry.Wrap(err)
				}
				continue
			}
			stepLocal := int64(t.Seconds())

			logger.Debug("will do query",
				zap.String("query", target),
				zap.Int64("start", start),
				zap.Int64("stop", stop),
				zap.String("step", stepLocalStr),
			)
			v := url.Values{
				"query": []string{target},
				"start": []string{strconv.Itoa(int(start))},
				"end":   []string{strconv.Itoa(int(stop))},
				"step":  []string{stepLocalStr},
			}

			rewrite.RawQuery = v.Encode()
			stats.RenderRequests++
			res, err2 := c.httpQuery.DoQuery(ctx, logger, rewrite.RequestURI(), nil)
			if err2 != nil {
				stats.RenderErrors++
				if merry.Is(err, types.ErrTimeoutExceeded) {
					stats.Timeouts++
					stats.RenderTimeouts++
				}
				if e == nil {
					e = err2
				} else {
					e = e.WithCause(err2)
				}
				continue
			}

			var response prometheusTypes.HTTPResponse
			err = json.Unmarshal(res.Response, &response)
			if err != nil {
				stats.RenderErrors += 1
				c.logger.Debug("failed to unmarshal response",
					zap.Error(err),
				)
				if e == nil {
					e = err2
				} else {
					e = e.WithCause(err2)
				}
				continue
			}

			if response.Status != "success" {
				stats.RenderErrors += 1
				if e == nil {
					e = types.ErrFailedToFetch.WithMessage(response.Status).WithValue("query", target).WithValue("status", response.Status)
				} else {
					e = e.WithCause(err2).WithValue("query", target).WithValue("status", response.Status)
				}
				continue
			}

			for _, m := range response.Data.Result {
				// We always should trust backend's response (to mimic behavior of graphite for grahpite native protoocols)
				// See https://github.com/go-graphite/carbonapi/issues/504 and https://github.com/go-graphite/carbonapi/issues/514
				realStart := start
				realStop := stop
				if len(m.Values) > 0 {
					realStart = int64(m.Values[0].Timestamp)
					realStop = int64(m.Values[len(m.Values)-1].Timestamp)
				}
				alignedValues := helpers.AlignValues(realStart, realStop, stepLocal, m.Values)

				r.Metrics = append(r.Metrics, protov3.FetchResponse{
					Name:              helpers.PromMetricToGraphite(m.Metric),
					PathExpression:    pathExpr,
					ConsolidationFunc: "Average",
					StartTime:         realStart,
					StopTime:          realStop,
					StepTime:          stepLocal,
					Values:            alignedValues,
					XFilesFactor:      0.0,
				})
			}
		}
	}

	if e != nil {
		stats.FailedServers = []string{c.groupName}
		logger.Error("errors occurred while getting results",
			zap.Any("errors", e),
		)
		return &r, stats, e
	}
	return &r, stats, nil
}
<commit_msg>victoriametrics: check for length of request.Metrics instead of just null<commit_after>package victoriametrics

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/ansel1/merry"
	"github.com/go-graphite/carbonapi/zipper/protocols/prometheus/helpers"
	prometheusTypes "github.com/go-graphite/carbonapi/zipper/protocols/prometheus/types"
	"github.com/go-graphite/carbonapi/zipper/types"
	protov3 "github.com/go-graphite/protocol/carbonapi_v3_pb"
	"go.uber.org/zap"
)

func (c *VictoriaMetricsGroup) Fetch(ctx context.Context, request *protov3.MultiFetchRequest) (*protov3.MultiFetchResponse, *types.Stats, merry.Error) {
	supportedFeatures, _ := c.featureSet.Load().(*vmSupportedFeatures)
	if !supportedFeatures.SupportOptimizedGraphiteFetch {
		// VictoriaMetrics <1.53.1 doesn't support graphite find api, reverting back to prometheus code-path
		return c.BackendServer.Fetch(ctx, request)
	}
	logger := c.logger.With(zap.String("type", "fetch"), zap.String("request", request.String()))
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/api/v1/query_range")

	pathExprToTargets := make(map[string][]string)
	for _, m := range request.Metrics {
		targets := pathExprToTargets[m.PathExpression]
		pathExprToTargets[m.PathExpression] = append(targets, m.Name)
	}

	var r protov3.MultiFetchResponse
	var e merry.Error

	start := request.Metrics[0].StartTime
	stop := request.Metrics[0].StopTime

	maxPointsPerQuery := c.maxPointsPerQuery
	if len(request.Metrics) > 0 && request.Metrics[0].MaxDataPoints != 0 {
		maxPointsPerQuery = request.Metrics[0].MaxDataPoints
	}
	step := helpers.AdjustStep(start, stop, maxPointsPerQuery, c.step)

	stepStr := strconv.FormatInt(step, 10)
	for pathExpr, targets := range pathExprToTargets {
		for _, target := range targets {
			logger.Debug("got some target to query",
				zap.Any("pathExpr", pathExpr),
				zap.Any("target", target),
			)
			// rewrite metric for Tag
			// Make local copy
			stepLocalStr := stepStr
			if strings.HasPrefix(target, "seriesByTag") {
				stepLocalStr, target = helpers.SeriesByTagToPromQL(stepLocalStr, target)
			} else {
				target = fmt.Sprintf("{__graphite__=%q}", target)
			}
			if stepLocalStr[len(stepLocalStr)-1] >= '0' && stepLocalStr[len(stepLocalStr)-1] <= '9' {
				stepLocalStr += "s"
			}
			t, err := time.ParseDuration(stepLocalStr)
			if err != nil {
				stats.RenderErrors += 1
				logger.Debug("failed to parse step",
					zap.String("step", stepLocalStr),
					zap.Error(err),
				)
				if e == nil {
					e = merry.Wrap(err)
				}
				continue
			}
			stepLocal := int64(t.Seconds())

			logger.Debug("will do query",
				zap.String("query", target),
				zap.Int64("start", start),
				zap.Int64("stop", stop),
				zap.String("step", stepLocalStr),
			)
			v := url.Values{
				"query": []string{target},
				"start": []string{strconv.Itoa(int(start))},
				"end":   []string{strconv.Itoa(int(stop))},
				"step":  []string{stepLocalStr},
			}

			rewrite.RawQuery = v.Encode()
			stats.RenderRequests++
			res, err2 := c.httpQuery.DoQuery(ctx, logger, rewrite.RequestURI(), nil)
			if err2 != nil {
				stats.RenderErrors++
				if merry.Is(err, types.ErrTimeoutExceeded) {
					stats.Timeouts++
					stats.RenderTimeouts++
				}
				if e == nil {
					e = err2
				} else {
					e = e.WithCause(err2)
				}
				continue
			}

			var response prometheusTypes.HTTPResponse
			err = json.Unmarshal(res.Response, &response)
			if err != nil {
				stats.RenderErrors += 1
				c.logger.Debug("failed to unmarshal response",
					zap.Error(err),
				)
				if e == nil {
					e = err2
				} else {
					e = e.WithCause(err2)
				}
				continue
			}

			if response.Status != "success" {
				stats.RenderErrors += 1
				if e == nil {
					e = types.ErrFailedToFetch.WithMessage(response.Status).WithValue("query", target).WithValue("status", response.Status)
				} else {
					e = e.WithCause(err2).WithValue("query", target).WithValue("status", response.Status)
				}
				continue
			}

			for _, m := range response.Data.Result {
				// We always should trust backend's response (to mimic behavior of graphite for grahpite native protoocols)
				// See https://github.com/go-graphite/carbonapi/issues/504 and https://github.com/go-graphite/carbonapi/issues/514
				realStart := start
				realStop := stop
				if len(m.Values) > 0 {
					realStart = int64(m.Values[0].Timestamp)
					realStop = int64(m.Values[len(m.Values)-1].Timestamp)
				}
				alignedValues := helpers.AlignValues(realStart, realStop, stepLocal, m.Values)

				r.Metrics = append(r.Metrics, protov3.FetchResponse{
					Name:              helpers.PromMetricToGraphite(m.Metric),
					PathExpression:    pathExpr,
					ConsolidationFunc: "Average",
					StartTime:         realStart,
					StopTime:          realStop,
					StepTime:          stepLocal,
					Values:            alignedValues,
					XFilesFactor:      0.0,
				})
			}
		}
	}

	if e != nil {
		stats.FailedServers = []string{c.groupName}
		logger.Error("errors occurred while getting results",
			zap.Any("errors", e),
		)
		return &r, stats, e
	}
	return &r, stats, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"

	ircevent "github.com/thoj/go-ircevent"
)

//irc client options struct
type ircConfig struct {
	Server   string
	Channels []string
	Nickname string
	Verbose  bool
	Debug    bool
}

var (
	ircConn *ircevent.Connection //irc connection
	ircCfg  *ircConfig           //irc config
	//regex parse string for newEpisode()
	releaseWatch = regexp.MustCompile(
		`(?i)release:.+\[horriblesubs\] (.+) - ([0-9]{1,3}) \[(1080p|720p|480p)\]`)
)

//Starts the irc client
func ircConnStart(c *ircConfig) {
	ircCfg = c

	ircConn = ircevent.IRC(c.Nickname, c.Nickname)
	ircConn.Debug = c.Debug
	ircConn.VerboseCallbackHandler = c.Verbose
	ircConn.AddCallback("PRIVMSG", ircMsgHandler)
	ircConn.AddCallback("001", ircWelcomeHandler)

	err := ircConn.Connect(c.Server)
	if err != nil {
		log.Fatal("ircConnStart() => Connect() error:\t", err)
	}
	ircConn.Loop()
}

//irc client incomming message handler function
func ircMsgHandler(e *ircevent.Event) {
	//Samples new anime string:
	//Release: [Anime] [HorribleSubs] Ushio to Tora - 22 [480p].mkv
	//Release: [Anime] [HorribleSubs] Hackadoll the Animation - 09 [720p].mkv

	if releaseWatch.MatchString(e.Message()) == true {
		newEpisode(releaseWatch.FindStringSubmatch(e.Message()))
	} else if e.Arguments[0] == ircClient.Nickname && e.Nick != ircClient.Nickname {
		ircConn.Privmsg(e.Nick, "Discord BOT - https://github.com/gagizagi/Axolotl-GO")
		ircConn.Privmsg(e.Nick, "Current uptime is "+getUptime())
	}
}

//irc client onWelcome handler function
func ircWelcomeHandler(e *ircevent.Event) {
	for _, channel := range ircCfg.Channels {
		ircConn.Join(channel)
	}
}

//new episode handler function
//args
// 0 = full message
// 1 = anime name
// 2 = episode (1-3 length integers only)
// 3 = resolution (1080p | 720p | 480p)
func newEpisode(args []string) {
	epnum, _ := strconv.Atoi(args[2])
	episode := anime{Name: args[1], Episode: epnum}
	if episode.Exists() {
		if episode.NewEpisode() {
			episode.UpdateEp()
			if len(episode.Subs) > 0 {
				resultstr := fmt.Sprintf(
					"**New episode of %s released - Episode %d**\n",
					episode.Name, episode.Episode)

				//Add mentions for every subbed user
				for _, person := range episode.Subs {
					resultstr += fmt.Sprintf("<@%s>", person)
				}

				//Add download link
				if episode.Href != "" {
					resultstr += fmt.Sprintf("\nDownload at %s\n", episode.Href)
				} else {
					resultstr += fmt.Sprint("\nDownload at http://horriblesubs.info/\n")
				}

				//Add subscribe ID
				resultstr += fmt.Sprintf(
					"To subscribe to this anime type \"!sub %s\"",
					episode.ID)

				//Send update message to all anime channels
				//TODO make a function
				for _, channel := range discordCfg.AnimeChannels {
					discord.ChannelMessageSend(channel, resultstr)
				}
			}
		}
	} else {
		//Insert series into DB
		episode.Insert()

		//Announce new series to all anime channels
		resultstr := fmt.Sprintf("**New series started: %s - Episode %d**\n",
			episode.Name, episode.Episode)
		resultstr += fmt.Sprintf("To subscribe to this anime type \"!sub %s\"",
			episode.ID)

		//TODO make a function
		for _, channel := range discordCfg.AnimeChannels {
			discord.ChannelMessageSend(channel, resultstr)
		}
	}
}
<commit_msg>Fixed irc response to work with different bot names<commit_after>package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"

	ircevent "github.com/thoj/go-ircevent"
)

//irc client options struct
type ircConfig struct {
	Server   string
	Channels []string
	Nickname string
	Verbose  bool
	Debug    bool
}

var (
	ircConn *ircevent.Connection //irc connection
	ircCfg  *ircConfig           //irc config
	//regex parse string for newEpisode()
	releaseWatch = regexp.MustCompile(
		`(?i)release:.+\[horriblesubs\] (.+) - ([0-9]{1,3}) \[(1080p|720p|480p)\]`)
)

//Starts the irc client
func ircConnStart(c *ircConfig) {
	ircCfg = c

	ircConn = ircevent.IRC(c.Nickname, c.Nickname)
	ircConn.Debug = c.Debug
	ircConn.VerboseCallbackHandler = c.Verbose
	ircConn.AddCallback("PRIVMSG", ircMsgHandler)
	ircConn.AddCallback("001", ircWelcomeHandler)

	err := ircConn.Connect(c.Server)
	if err != nil {
		log.Fatal("ircConnStart() => Connect() error:\t", err)
	}
	ircConn.Loop()
}

//irc client incomming message handler function
func ircMsgHandler(e *ircevent.Event) {
	//Samples new anime string:
	//Release: [Anime] [HorribleSubs] Ushio to Tora - 22 [480p].mkv
	//Release: [Anime] [HorribleSubs] Hackadoll the Animation - 09 [720p].mkv

	if releaseWatch.MatchString(e.Message()) == true {
		newEpisode(releaseWatch.FindStringSubmatch(e.Message()))
	} else if e.Arguments[0] == ircConn.GetNick() && e.Nick != ircConn.GetNick() {
		ircConn.Privmsg(e.Nick, "Discord BOT - https://github.com/gagizagi/Axolotl-GO")
		ircConn.Privmsg(e.Nick, "Current uptime is "+getUptime())
	}
}

//irc client onWelcome handler function
func ircWelcomeHandler(e *ircevent.Event) {
	for _, channel := range ircCfg.Channels {
		ircConn.Join(channel)
	}
}

//new episode handler function
//args
// 0 = full message
// 1 = anime name
// 2 = episode (1-3 length integers only)
// 3 = resolution (1080p | 720p | 480p)
func newEpisode(args []string) {
	epnum, _ := strconv.Atoi(args[2])
	episode := anime{Name: args[1], Episode: epnum}
	if episode.Exists() {
		if episode.NewEpisode() {
			episode.UpdateEp()
			if len(episode.Subs) > 0 {
				resultstr := fmt.Sprintf(
					"**New episode of %s released - Episode %d**\n",
					episode.Name, episode.Episode)

				//Add mentions for every subbed user
				for _, person := range episode.Subs {
					resultstr += fmt.Sprintf("<@%s>", person)
				}

				//Add download link
				if episode.Href != "" {
					resultstr += fmt.Sprintf("\nDownload at %s\n", episode.Href)
				} else {
					resultstr += fmt.Sprint("\nDownload at http://horriblesubs.info/\n")
				}

				//Add subscribe ID
				resultstr += fmt.Sprintf(
					"To subscribe to this anime type \"!sub %s\"",
					episode.ID)

				//Send update message to all anime channels
				//TODO make a function
				for _, channel := range discordCfg.AnimeChannels {
					discord.ChannelMessageSend(channel, resultstr)
				}
			}
		}
	} else {
		//Insert series into DB
		episode.Insert()

		//Announce new series to all anime channels
		resultstr := fmt.Sprintf("**New series started: %s - Episode %d**\n",
			episode.Name, episode.Episode)
		resultstr += fmt.Sprintf("To subscribe to this anime type \"!sub %s\"",
			episode.ID)

		//TODO make a function
		for _, channel := range discordCfg.AnimeChannels {
			discord.ChannelMessageSend(channel, resultstr)
		}
	}
}
<|endoftext|>