<commit_before>// Copyright 2013 Matthew Baird
// 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 elastigo

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

// Query creates a new Query Dsl
func Query() *QueryDsl {
	return &QueryDsl{}
}

/*

some ways to serialize
"query": {
	"filtered": {
	  "query": {
	    "query_string": {
	      "default_operator": "OR",
	      "default_field": "_all",
	      "query": " actor:\"bob\"  AND type:\"EventType\""
	    }
	  },
	  "filter": {
	    "range": {
	      "@timestamp": {
	        "from": "2012-12-29T16:52:48+00:00",
	        "to": "2012-12-29T17:52:48+00:00"
	      }
	    }
	  }
	}
},

"query" : {
    "term" : { "user" : "kimchy" }
}

"query" : {
    "match_all" : {}
},
*/
type QueryDsl struct {
	QueryEmbed
	FilterVal *FilterOp `json:"filter,omitempty"`
}

// The core Query Syntax can be embedded as a child of a variety of different parents
type QueryEmbed struct {
	MatchAll   *MatchAll         `json:"match_all,omitempty"`
	Terms      map[string]string `json:"term,omitempty"`
	Qs         *QueryString      `json:"query_string,omitempty"`
	MultiMatch *MultiMatch       `json:"multi_match,omitempty"`
	//Exist    string            `json:"_exists_,omitempty"`
}

// MarshalJSON provides custom marshalling to support the query dsl which is a conditional
// json format, not always the same parent/children
func (qd *QueryDsl) MarshalJSON() ([]byte, error) {
	q := qd.QueryEmbed
	hasQuery := false
	if q.Qs != nil || len(q.Terms) > 0 || q.MatchAll != nil || q.MultiMatch != nil {
		hasQuery = true
	}
	// If a query has a
	if qd.FilterVal != nil && hasQuery {
		queryB, err := json.Marshal(q)
		if err != nil {
			return queryB, err
		}
		filterB, err := json.Marshal(qd.FilterVal)
		if err != nil {
			return filterB, err
		}
		return []byte(fmt.Sprintf(`{"filtered":{"query":%s,"filter":%s}}`, queryB, filterB)), nil
	}
	return json.Marshal(q)
}

// get all
func (q *QueryDsl) All() *QueryDsl {
	q.MatchAll = &MatchAll{""}
	return q
}

// Limit the query to this range
func (q *QueryDsl) Range(fop *FilterOp) *QueryDsl {
	if q.FilterVal == nil {
		q.FilterVal = fop
		return q
	}
	// TODO:  this is not valid, refactor
	q.FilterVal.Add(fop)
	return q
}

// Add a term search for a specific field
//    Term("user","kimchy")
func (q *QueryDsl) Term(name, value string) *QueryDsl {
	if len(q.Terms) == 0 {
		q.Terms = make(map[string]string)
	}
	q.Terms[name] = value
	return q
}

// The raw search strings (lucene valid)
func (q *QueryDsl) Search(searchFor string) *QueryDsl {
	//I don't think this is right, it is not a filter.query, it should be q query?
	qs := NewQueryString("", "")
	q.QueryEmbed.Qs = &qs
	q.QueryEmbed.Qs.Query = searchFor
	return q
}

// Querystring operations
func (q *QueryDsl) Qs(qs *QueryString) *QueryDsl {
	q.QueryEmbed.Qs = qs
	return q
}

// Fields in query_string search
//     Fields("fieldname","search_for","","")
//
//     Fields("fieldname,field2,field3","search_for","","")
//
//     Fields("fieldname,field2,field3","search_for","field_exists","")
func (q *QueryDsl) Fields(fields, search, exists, missing string) *QueryDsl {
	fieldList := strings.Split(fields, ",")
	qs := NewQueryString("", "")
	q.QueryEmbed.Qs = &qs
	q.QueryEmbed.Qs.Query = search
	if len(fieldList) == 1 {
		q.QueryEmbed.Qs.DefaultField = fields
	} else {
		q.QueryEmbed.Qs.Fields = fieldList
	}
	q.QueryEmbed.Qs.Exists = exists
	q.QueryEmbed.Qs.Missing = missing
	return q
}

// Filter this query
func (q *QueryDsl) Filter(f *FilterOp) *QueryDsl {
	q.FilterVal = f
	return q
}

// MultiMatch allows searching against multiple fields.
func (q *QueryDsl) MultiMatch(s string, fields []string) *QueryDsl {
	q.QueryEmbed.MultiMatch = &MultiMatch{Query: s, Fields: fields}
	return q
}

type MultiMatch struct {
	Query  string   `json:"query"`
	Fields []string `json:"fields"`
}

type MatchAll struct {
	All string `json:"-"`
}

// should we reuse QueryDsl here?
type QueryWrap struct {
	Qs QueryString `json:"query_string,omitempty"`
}

// QueryString based search
func NewQueryString(field, query string) QueryString {
	return QueryString{"", field, query, "", "", nil}
}

type QueryString struct {
	DefaultOperator string   `json:"default_operator,omitempty"`
	DefaultField    string   `json:"default_field,omitempty"`
	Query           string   `json:"query,omitempty"`
	Exists          string   `json:"_exists_,omitempty"`
	Missing         string   `json:"_missing_,omitempty"`
	Fields          []string `json:"fields,omitempty"`
	//_exists_:field1,
	//_missing_:field1,
}

// Generic Term based (used in query, facet, filter)
type Term struct {
	Terms     Terms       `json:"terms,omitempty"`
	FilterVal *FilterWrap `json:"facet_filter,omitempty"`
}

type Terms struct {
	Fields []string `json:"field,omitempty"`
	Size   string   `json:"size,omitempty"`
	Regex  string   `json:"regex,omitempty"`
}

func NewTerm(fields ...string) *Term {
	m := &Term{Terms{Fields: fields}, nil}
	return m
}

func (s *Term) Filter(fl ...interface{}) *Term {
	if s.FilterVal == nil {
		s.FilterVal = NewFilterWrap()
	}

	s.FilterVal.addFilters(fl)
	return s
}

// Custom marshalling
func (t *Terms) MarshalJSON() ([]byte, error) {
	m := make(map[string]interface{})
	// TODO:  this isn't getting called!?
	if len(t.Fields) == 1 {
		m["field"] = t.Fields[0]
	} else if len(t.Fields) > 1 {
		m["fields"] = t.Fields
	}
	if len(t.Regex) > 0 {
		m["regex"] = t.Regex
	}
	if len(t.Size) > 0 {
		m["size"] = t.Size
	}
	return json.Marshal(m)
}
<commit_msg>Add support for function score on query dsl<commit_after>// Copyright 2013 Matthew Baird
// 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 elastigo

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

// Query creates a new Query Dsl
func Query() *QueryDsl {
	return &QueryDsl{}
}

/*

some ways to serialize
"query": {
	"filtered": {
	  "query": {
	    "query_string": {
	      "default_operator": "OR",
	      "default_field": "_all",
	      "query": " actor:\"bob\"  AND type:\"EventType\""
	    }
	  },
	  "filter": {
	    "range": {
	      "@timestamp": {
	        "from": "2012-12-29T16:52:48+00:00",
	        "to": "2012-12-29T17:52:48+00:00"
	      }
	    }
	  }
	}
},

"query" : {
    "term" : { "user" : "kimchy" }
}

"query" : {
    "match_all" : {}
},
*/
type QueryDsl struct {
	QueryEmbed
	FilterVal *FilterOp `json:"filter,omitempty"`
}

// The core Query Syntax can be embedded as a child of a variety of different parents
type QueryEmbed struct {
	MatchAll      *MatchAll              `json:"match_all,omitempty"`
	Terms         map[string]string      `json:"term,omitempty"`
	Qs            *QueryString           `json:"query_string,omitempty"`
	MultiMatch    *MultiMatch            `json:"multi_match,omitempty"`
	FunctionScore map[string]interface{} `json:"function_score,omitempty"`
	//Exist    string            `json:"_exists_,omitempty"`
}

// MarshalJSON provides custom marshalling to support the query dsl which is a conditional
// json format, not always the same parent/children
func (qd *QueryDsl) MarshalJSON() ([]byte, error) {
	q := qd.QueryEmbed
	hasQuery := false
	if q.Qs != nil || len(q.Terms) > 0 || q.MatchAll != nil || q.MultiMatch != nil {
		hasQuery = true
	}
	// If a query has a
	if qd.FilterVal != nil && hasQuery {
		queryB, err := json.Marshal(q)
		if err != nil {
			return queryB, err
		}
		filterB, err := json.Marshal(qd.FilterVal)
		if err != nil {
			return filterB, err
		}
		return []byte(fmt.Sprintf(`{"filtered":{"query":%s,"filter":%s}}`, queryB, filterB)), nil
	}
	return json.Marshal(q)
}

// get all
func (q *QueryDsl) All() *QueryDsl {
	q.MatchAll = &MatchAll{""}
	return q
}

// Limit the query to this range
func (q *QueryDsl) Range(fop *FilterOp) *QueryDsl {
	if q.FilterVal == nil {
		q.FilterVal = fop
		return q
	}
	// TODO:  this is not valid, refactor
	q.FilterVal.Add(fop)
	return q
}

// Add a term search for a specific field
//    Term("user","kimchy")
func (q *QueryDsl) Term(name, value string) *QueryDsl {
	if len(q.Terms) == 0 {
		q.Terms = make(map[string]string)
	}
	q.Terms[name] = value
	return q
}

// FunctionScore sets functions to use to score the documents.
// http://www.elastic.co/guide/en/elasticsearch/reference/1.x/query-dsl-function-score-query.html
func (q *QueryDsl) FunctionScore(mode string, functions ...map[string]interface{}) *QueryDsl {
	q.QueryEmbed.FunctionScore = map[string]interface{}{
		"functions":  functions,
		"score_mode": mode,
	}
	return q
}

// The raw search strings (lucene valid)
func (q *QueryDsl) Search(searchFor string) *QueryDsl {
	//I don't think this is right, it is not a filter.query, it should be q query?
	qs := NewQueryString("", "")
	q.QueryEmbed.Qs = &qs
	q.QueryEmbed.Qs.Query = searchFor
	return q
}

// Querystring operations
func (q *QueryDsl) Qs(qs *QueryString) *QueryDsl {
	q.QueryEmbed.Qs = qs
	return q
}

// Fields in query_string search
//     Fields("fieldname","search_for","","")
//
//     Fields("fieldname,field2,field3","search_for","","")
//
//     Fields("fieldname,field2,field3","search_for","field_exists","")
func (q *QueryDsl) Fields(fields, search, exists, missing string) *QueryDsl {
	fieldList := strings.Split(fields, ",")
	qs := NewQueryString("", "")
	q.QueryEmbed.Qs = &qs
	q.QueryEmbed.Qs.Query = search
	if len(fieldList) == 1 {
		q.QueryEmbed.Qs.DefaultField = fields
	} else {
		q.QueryEmbed.Qs.Fields = fieldList
	}
	q.QueryEmbed.Qs.Exists = exists
	q.QueryEmbed.Qs.Missing = missing
	return q
}

// Filter this query
func (q *QueryDsl) Filter(f *FilterOp) *QueryDsl {
	q.FilterVal = f
	return q
}

// MultiMatch allows searching against multiple fields.
func (q *QueryDsl) MultiMatch(s string, fields []string) *QueryDsl {
	q.QueryEmbed.MultiMatch = &MultiMatch{Query: s, Fields: fields}
	return q
}

type MultiMatch struct {
	Query  string   `json:"query"`
	Fields []string `json:"fields"`
}

type MatchAll struct {
	All string `json:"-"`
}

// should we reuse QueryDsl here?
type QueryWrap struct {
	Qs QueryString `json:"query_string,omitempty"`
}

// QueryString based search
func NewQueryString(field, query string) QueryString {
	return QueryString{"", field, query, "", "", nil}
}

type QueryString struct {
	DefaultOperator string   `json:"default_operator,omitempty"`
	DefaultField    string   `json:"default_field,omitempty"`
	Query           string   `json:"query,omitempty"`
	Exists          string   `json:"_exists_,omitempty"`
	Missing         string   `json:"_missing_,omitempty"`
	Fields          []string `json:"fields,omitempty"`
	//_exists_:field1,
	//_missing_:field1,
}

// Generic Term based (used in query, facet, filter)
type Term struct {
	Terms     Terms       `json:"terms,omitempty"`
	FilterVal *FilterWrap `json:"facet_filter,omitempty"`
}

type Terms struct {
	Fields []string `json:"field,omitempty"`
	Size   string   `json:"size,omitempty"`
	Regex  string   `json:"regex,omitempty"`
}

func NewTerm(fields ...string) *Term {
	m := &Term{Terms{Fields: fields}, nil}
	return m
}

func (s *Term) Filter(fl ...interface{}) *Term {
	if s.FilterVal == nil {
		s.FilterVal = NewFilterWrap()
	}

	s.FilterVal.addFilters(fl)
	return s
}

// Custom marshalling
func (t *Terms) MarshalJSON() ([]byte, error) {
	m := make(map[string]interface{})
	// TODO:  this isn't getting called!?
	if len(t.Fields) == 1 {
		m["field"] = t.Fields[0]
	} else if len(t.Fields) > 1 {
		m["fields"] = t.Fields
	}
	if len(t.Regex) > 0 {
		m["regex"] = t.Regex
	}
	if len(t.Size) > 0 {
		m["size"] = t.Size
	}
	return json.Marshal(m)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/bwmarrin/discordgo"
	"github.com/bwmarrin/dgvoice"
	"os"
	"strings"
	"os/signal"
	"syscall"
	"time"
	"strconv"
	"os/exec"
	"fmt"
)

var plm map[string]*server

func main() {
	discord, _ := discordgo.New("Bot MTg5MTQ2MDg0NzE3NjI1MzQ0.DANL1A.4cLruFPliFxkd0r41pYB307_D1M")
	discord.Open()
	//discord.ChannelMessageSend("104979971667197952", "*hello there*")

	discord.AddHandler(messageCreate)

	plm = make(map[string]*server)

	sc := make(chan os.Signal, 1)
	//noinspection ALL
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc

	discord.Close()

}

func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if m.Author.ID == s.State.User.ID {
		return
	}
	if strings.HasPrefix(m.Content, "!echo") {
		s.ChannelMessageSend(m.ChannelID, m.Content)

	}
	if m.Author.Bot {
		s.ChannelMessageSend(m.ChannelID, m.Author.Mention()+" ur geay")
	}
	if strings.HasPrefix(m.Content, "!botsay") {
		s.ChannelMessageSend(m.ChannelID, strings.TrimPrefix(m.Content, "!botsay"))
		s.ChannelMessageDelete(m.ChannelID, m.ID)
	}

	if strings.HasPrefix(m.Content, "!sr") {
		defer func() {
			if r := recover(); r != nil{
				s.ChannelMessageSend(m.ID, "Hmm, we couldn't find a youtube video with that link")
			}
		}()
		request := parseLink(strings.TrimSpace(strings.TrimPrefix(m.Content,"!sr"))) //Requested song/link

		c, _ := s.State.Channel(m.ChannelID)
		se := plm[c.GuildID] //Saves server locally

		if se == nil { //Initializes server
			se = new(server)
			se.pl = make([]string, 0)
			se.connect(s, c)
		}

		if !songExists(request){ //Download
			go download(request)
		}

		se.pl = append(se.pl, request) //Adds item to playlist

		plm[c.GuildID] = se

	}

	if strings.HasPrefix(m.Content, "!pll"){
		c, _ := s.State.Channel(m.ChannelID)

		s.ChannelMessageSend(m.ChannelID, strconv.Itoa(len(plm[c.GuildID].pl)))
	}
	if strings.HasPrefix(m.Content, "!skip"){

		if m.Content == "!skip"{
			dgvoice.KillPlayer()
		}else{
			a := strings.TrimSpace(strings.TrimPrefix(m.Content, "!skip"))
			i, err := strconv.Atoi(a)
			if err != nil {
				return
			}
			if i < 0{
				c, _ := s.State.Channel(m.ChannelID)
				se := plm[c.GuildID] //Saves server locally

				se.pl = append(se.pl[:i], se.pl[i+1:]...)
			}else if i == 0{
				m.Content = "!skip"
				messageCreate(s, m)
			}
		}
	}
}

func (se *server) playLoop(s *discordgo.Session) {
	for{
		for len(se.pl)==0{
			time.Sleep(time.Second*1)
		}

		for !songExists(se.pl[0]){
			time.Sleep(time.Second*1)
		}

		se.playFile()
		npl := make([]string, len(se.pl)-1)
		for i := range se.pl{
			if i==0{
				continue
			}
			npl[i-1] = se.pl[i]
		}
		se.pl = npl

	}
}

func (se *server)playFile() {
	se.playing = true
	fmt.Println("Playing")
	dgvoice.PlayAudioFile(se.dgv, se.pl[0]+".mp3")
	se.playing = false
	fmt.Println("Stopped playing")
}

func (se *server) connect(s *discordgo.Session, c *discordgo.Channel) {
	g, _ := s.State.Guild(c.GuildID)
	dgv, _ := s.ChannelVoiceJoin(g.ID, g.VoiceStates[0].ChannelID, false,false)
	se.dgv = dgv
	go se.playLoop(s)
	return

}

type server struct{
	dgv *discordgo.VoiceConnection
	pl  []string
	playing bool

}

func download(s string){
	cmd := exec.Command("youtube-dl", "--extract-audio", "--audio-format", "mp3", "--output", ""+s+".mp3" ,s)

	// Combine stdout and stderr
	printCommand(cmd)
	output, err := cmd.CombinedOutput()
	printError(err)
	printOutput(output) // => go version go1.3 darwin/amd64

}

func songExists(s string) bool{
	if _, err := os.Stat(s+".mp3"); os.IsNotExist(err) { //Download
		return false
	}else{
		return true
	}
}
func printCommand(cmd *exec.Cmd) {
	fmt.Printf("==> Executing: %s\n", strings.Join(cmd.Args, " "))
}
func printError(err error) {
	if err != nil {
		os.Stderr.WriteString(fmt.Sprintf("==> Error: %s\n", err.Error()))
	}
}
func printOutput(outs []byte) {
	if len(outs) > 0 {
		fmt.Printf("==> Output: %s\n", string(outs))
	}
}

func parseLink(s string) string{

	s = strings.TrimPrefix(s, "https://")
	s = strings.TrimPrefix(s, "http://")
	s = strings.TrimPrefix(s, "www.")

	if len(s) == 11{
		return s
	}else if strings.Contains(s, "youtube.com"){
		s = strings.TrimPrefix(s, "youtube.com/watch?v=")
		s = strings.Split(s,"&")[0]
	}else if strings.Contains(s, "youtu.be"){
		s = strings.TrimPrefix(s, "youtu.be/")
		s = strings.Split(s, "?")[0]
	}else{
		panic("No video found")
	}
	return s

}<commit_msg>Cleaned up code, removed shitty jokes, added functionallity to playlist command<commit_after>package main

import (
	"github.com/bwmarrin/discordgo"
	"github.com/bwmarrin/dgvoice"
	"os"
	"strings"
	"os/signal"
	"syscall"
	"time"
	"strconv"
	"os/exec"
	"fmt"
)

var plm map[string]*server

func main() {
	discord, _ := discordgo.New("Bot MTg5MTQ2MDg0NzE3NjI1MzQ0.DANL1A.4cLruFPliFxkd0r41pYB307_D1M")
	discord.Open()
	//discord.ChannelMessageSend("104979971667197952", "*hello there*")

	discord.AddHandler(messageCreate)

	plm = make(map[string]*server)

	sc := make(chan os.Signal, 1)
	//noinspection ALL
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc

	discord.Close()

}

func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if m.Author.ID == s.State.User.ID {
		return
	}

	if strings.HasPrefix(m.Content, "!botsay") {
		s.ChannelMessageSend(m.ChannelID, strings.TrimPrefix(m.Content, "!botsay"))
		s.ChannelMessageDelete(m.ChannelID, m.ID)
	}

	if strings.HasPrefix(m.Content, "!sr") {
		defer func() {
			if r := recover(); r != nil {
				s.ChannelMessageSend(m.ID, "Hmm, we couldn't find a youtube video with that link")
			}
		}()
		request := parseLink(strings.TrimSpace(strings.TrimPrefix(m.Content, "!sr"))) //Requested song/link

		c, _ := s.State.Channel(m.ChannelID)
		se := plm[c.GuildID] //Saves server locally

		if se == nil { //Initializes server
			se = new(server)
			se.pl = make([]string, 0)
			se.connect(s, c)
		}

		if !songExists(request) { //Download
			go download(request)
		}

		se.pl = append(se.pl, request) //Adds item to playlist

		plm[c.GuildID] = se

		s.ChannelMessageDelete(m.ChannelID, m.ID) //Deletes message

	}

	if strings.HasPrefix(m.Content, "!pll") || strings.HasPrefix(m.Content, "!playlist") || strings.HasPrefix(m.Content, "!pl") {
		c, _ := s.State.Channel(m.ChannelID)

		s.ChannelMessageSend(m.ChannelID, "There are "+strconv.Itoa(len(plm[c.GuildID].pl)))
	}

	if strings.HasPrefix(m.Content, "!skip") {

		if m.Content == "!skip" {
			dgvoice.KillPlayer()
		} else {
			a := strings.TrimSpace(strings.TrimPrefix(m.Content, "!skip"))
			i, err := strconv.Atoi(a)
			if err != nil {
				return
			}
			if i < 0 {
				c, _ := s.State.Channel(m.ChannelID)
				se := plm[c.GuildID] //Saves server locally

				se.pl = append(se.pl[:i], se.pl[i+1:]...)
			} else if i == 0 {
				m.Content = "!skip"
				messageCreate(s, m)
			}
		}
	}
}

type server struct {
	dgv     *discordgo.VoiceConnection
	pl      []string
	playing bool
}

func (se *server) connect(s *discordgo.Session, c *discordgo.Channel) {
	g, _ := s.State.Guild(c.GuildID)
	dgv, _ := s.ChannelVoiceJoin(g.ID, g.VoiceStates[0].ChannelID, false, false)
	se.dgv = dgv
	go se.playLoop(s)
	return

}

func (se *server) playLoop(s *discordgo.Session) {
	for {
		for len(se.pl) == 0 {
			time.Sleep(time.Second * 1)
		}

		for !songExists(se.pl[0]) {
			time.Sleep(time.Second * 1)
		}

		se.playFile()
		npl := make([]string, len(se.pl)-1)
		for i := range se.pl {
			if i == 0 {
				continue
			}
			npl[i-1] = se.pl[i]
		}
		se.pl = npl

	}
}

func (se *server) playFile() {
	se.playing = true
	fmt.Println("Playing")
	dgvoice.PlayAudioFile(se.dgv, se.pl[0]+".mp3")
	se.playing = false
	fmt.Println("Stopped playing")
}

func download(s string) {
	cmd := exec.Command("youtube-dl", "--extract-audio", "--audio-format", "mp3", "--output", s+".mp3", s)

	// Combine stdout and stderr
	fmt.Println(cmd)
	output, err := cmd.CombinedOutput()
	fmt.Println(err)
	fmt.Println(output) // => go version go1.3 darwin/amd64

}

func songExists(s string) bool {
	if _, err := os.Stat(s + ".mp3"); os.IsNotExist(err) { //Download
		return false
	} else {
		return true
	}
}

func parseLink(s string) string {

	s = strings.TrimPrefix(s, "https://")
	s = strings.TrimPrefix(s, "http://")
	s = strings.TrimPrefix(s, "www.")

	if len(s) == 11 {
		return s
	} else if strings.Contains(s, "youtube.com") {
		s = strings.TrimPrefix(s, "youtube.com/watch?v=")
		s = strings.Split(s, "&")[0]
	} else if strings.Contains(s, "youtu.be") {
		s = strings.TrimPrefix(s, "youtu.be/")
		s = strings.Split(s, "?")[0]
	} else {
		panic("No video found")
	}
	return s

}
<|endoftext|>


<commit_before>// Alexandria
//
// Copyright (C) 2015-2016  Colin Benner
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package alexandria

import (
	"fmt"
	"io/ioutil"
	"os"
)

func LogError(err interface{}) {
	fmt.Fprintln(os.Stderr, err)
}

// If an error occurred, log it.
func TryLogError(err interface{}) {
	if err != nil {
		LogError(err)
	}
}

// Load the content of a given scroll from disk.
func readScroll(id Id) (string, error) {
	result, err := ioutil.ReadFile(Config.KnowledgeDirectory + string(id) + ".tex")
	return string(result), err
}

// Load the content of a template file with the given name.
func readTemplate(filename string) (string, error) {
	result, err := ioutil.ReadFile(Config.TemplateDirectory + filename + ".tex")
	return string(result), err
}

// Write a TeX file with the given name and content to Alexandria's temp
// directory.
func writeTemp(id Id, data string) error {
	return ioutil.WriteFile(Config.TempDirectory+string(id)+".tex", []byte(data), 0644)
}

// Compute the combined size of all files in a given directory.
func getDirSize(dir string) (int, int64) {
	directory, err := os.Open(dir)
	TryLogError(err)
	defer directory.Close()
	fileInfo, err := directory.Readdir(0)
	if err != nil {
		panic(err)
	}
	result := int64(0)
	for _, file := range fileInfo {
		result += file.Size()
	}
	return len(fileInfo), result
}

// Get the time a given file was last modified as a Unix time.
func getModTime(file string) (int64, error) {
	info, err := os.Stat(file)
	if err != nil {
		return -1, err
	}
	return info.ModTime().Unix(), nil
}

// Cache the newest modification of any of the template files as a Unix time
// (i.e. seconds since 1970-01-01).
var templatesModTime int64 = -1

// All recognized template files
// TODO Generate the list⁈
var templateFiles []string = []string{
	"header.tex", "footer.tex",
	"algorithm_header.tex", "algorithm_footer.tex",
	"axiom_header.tex", "axiom_footer.tex",
	"corollary_header.tex", "corollary_footer.tex",
	"definition_header.tex", "definition_footer.tex",
	"example_header.tex", "example_footer.tex",
	"exercise_header.tex", "exercise_footer.tex",
	"lemma_header.tex", "lemma_footer.tex",
	"proof_header.tex", "proof_footer.tex",
	"proposition_header.tex", "proposition_footer.tex",
	"remark_header.tex", "remark_footer.tex",
	"theorem_header.tex", "theorem_footer.tex"}

// Check whether a given scroll has to be recompiled
func isUpToDate(id Id) bool {
	if templatesModTime == -1 {
		// Check template for modification times
		templatesModTime = 0

		for _, file := range templateFiles {
			foo, err := getModTime(Config.TemplateDirectory + file)
			if err != nil {
				break
			}
			if foo > templatesModTime {
				templatesModTime = foo
			}
		}
	}

	info, err := os.Stat(Config.CacheDirectory + string(id) + ".png")
	if err != nil {
		return false
	}
	imageTime := info.ModTime().Unix()

	if imageTime < templatesModTime {
		return false
	}

	info, err = os.Stat(Config.KnowledgeDirectory + string(id) + ".tex")
	if err != nil {
		return false // When in doubt, recompile
	}
	scrollTime := info.ModTime().Unix()

	return imageTime > scrollTime
}
<commit_msg>Move LaTeX templates to templates/tex<commit_after>// Alexandria
//
// Copyright (C) 2015-2016  Colin Benner
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package alexandria

import (
	"fmt"
	"io/ioutil"
	"os"
)

func LogError(err interface{}) {
	fmt.Fprintln(os.Stderr, err)
}

// If an error occurred, log it.
func TryLogError(err interface{}) {
	if err != nil {
		LogError(err)
	}
}

// Load the content of a given scroll from disk.
func readScroll(id Id) (string, error) {
	result, err := ioutil.ReadFile(Config.KnowledgeDirectory + string(id) + ".tex")
	return string(result), err
}

// Load the content of a template file with the given name.
func readTemplate(filename string) (string, error) {
	result, err := ioutil.ReadFile(Config.TemplateDirectory + "tex/" + filename + ".tex")
	return string(result), err
}

// Write a TeX file with the given name and content to Alexandria's temp
// directory.
func writeTemp(id Id, data string) error {
	return ioutil.WriteFile(Config.TempDirectory+string(id)+".tex", []byte(data), 0644)
}

// Compute the combined size of all files in a given directory.
func getDirSize(dir string) (int, int64) {
	directory, err := os.Open(dir)
	TryLogError(err)
	defer directory.Close()
	fileInfo, err := directory.Readdir(0)
	if err != nil {
		panic(err)
	}
	result := int64(0)
	for _, file := range fileInfo {
		result += file.Size()
	}
	return len(fileInfo), result
}

// Get the time a given file was last modified as a Unix time.
func getModTime(file string) (int64, error) {
	info, err := os.Stat(file)
	if err != nil {
		return -1, err
	}
	return info.ModTime().Unix(), nil
}

// Cache the newest modification of any of the template files as a Unix time
// (i.e. seconds since 1970-01-01).
var templatesModTime int64 = -1

// All recognized template files
// TODO Generate the list⁈
var templateFiles []string = []string{
	"header.tex", "footer.tex",
	"algorithm_header.tex", "algorithm_footer.tex",
	"axiom_header.tex", "axiom_footer.tex",
	"corollary_header.tex", "corollary_footer.tex",
	"definition_header.tex", "definition_footer.tex",
	"example_header.tex", "example_footer.tex",
	"exercise_header.tex", "exercise_footer.tex",
	"lemma_header.tex", "lemma_footer.tex",
	"proof_header.tex", "proof_footer.tex",
	"proposition_header.tex", "proposition_footer.tex",
	"remark_header.tex", "remark_footer.tex",
	"theorem_header.tex", "theorem_footer.tex"}

// Check whether a given scroll has to be recompiled
func isUpToDate(id Id) bool {
	if templatesModTime == -1 {
		// Check template for modification times
		templatesModTime = 0

		for _, file := range templateFiles {
			foo, err := getModTime(Config.TemplateDirectory + file)
			if err != nil {
				break
			}
			if foo > templatesModTime {
				templatesModTime = foo
			}
		}
	}

	info, err := os.Stat(Config.CacheDirectory + string(id) + ".png")
	if err != nil {
		return false
	}
	imageTime := info.ModTime().Unix()

	if imageTime < templatesModTime {
		return false
	}

	info, err = os.Stat(Config.KnowledgeDirectory + string(id) + ".tex")
	if err != nil {
		return false // When in doubt, recompile
	}
	scrollTime := info.ModTime().Unix()

	return imageTime > scrollTime
}
<|endoftext|>


<commit_before>package bitbucket

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

	"gopkg.in/go-playground/webhooks.v3"
)

// Webhook instance contains all methods needed to process events
type Webhook struct {
	provider   webhooks.Provider
	uuid       string
	eventFuncs map[Event]webhooks.ProcessPayloadFunc
}

// Config defines the configuration to create a new Bitbucket Webhook instance
type Config struct {
	UUID string
}

// Event defines a Bitbucket hook event type
type Event string

// Bitbucket hook types
const (
	RepoPushEvent                  Event = "repo:push"
	RepoForkEvent                  Event = "repo:fork"
	RepoUpdatedEvent               Event = "repo:updated"
	RepoCommitCommentCreatedEvent  Event = "repo:commit_comment_created"
	RepoCommitStatusCreatedEvent   Event = "repo:commit_status_created"
	RepoCommitStatusUpdatedEvent   Event = "repo:commit_status_updated"
	IssueCreatedEvent              Event = "issue:created"
	IssueUpdatedEvent              Event = "issue:updated"
	IssueCommentCreatedEvent       Event = "issue:comment_created"
	PullRequestCreatedEvent        Event = "pullrequest:created"
	PullRequestUpdatedEvent        Event = "pullrequest:updated"
	PullRequestApprovedEvent       Event = "pullrequest:approved"
	PullRequestUnapprovedEvent     Event = "pullrequest:unapproved"
	PullRequestMergedEvent         Event = "pullrequest:fulfilled"
	PullRequestDeclinedEvent       Event = "pullrequest:rejected"
	PullRequestCommentCreatedEvent Event = "pullrequest:comment_created"
	PullRequestCommentUpdatedEvent Event = "pullrequest:comment_updated"
	PullRequestCommentDeletedEvent Event = "pull_request:comment_deleted"
)

// New creates and returns a WebHook instance denoted by the Provider type
func New(config *Config) *Webhook {
	return &Webhook{
		provider:   webhooks.Bitbucket,
		uuid:       config.UUID,
		eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
	}
}

// Provider returns the current hooks provider ID
func (hook Webhook) Provider() webhooks.Provider {
	return hook.provider
}

// RegisterEvents registers the function to call when the specified event(s) are encountered
func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {

	for _, event := range events {
		hook.eventFuncs[event] = fn
	}
}

// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
	webhooks.DefaultLog.Info("Parsing Payload...")

	uuid := r.Header.Get("X-Hook-UUID")
	if uuid == "" {
		webhooks.DefaultLog.Error("Missing X-Hook-UUID Header")
		http.Error(w, "400 Bad Request - Missing X-Hook-UUID Header", http.StatusBadRequest)
		return
	}
	webhooks.DefaultLog.Debug(fmt.Sprintf("X-Hook-UUID:%s", uuid))

	if len(hook.uuid) > 0 {
		if uuid != hook.uuid {
			webhooks.DefaultLog.Error(fmt.Sprintf("X-Hook-UUID %s does not match configured uuid of %s", uuid, hook.uuid))
			http.Error(w, "403 Forbidden - X-Hook-UUID does not match", http.StatusForbidden)
			return
		}
	} else {
		webhooks.DefaultLog.Debug("hook uuid not defined - recommend setting for improved security")
	}

	event := r.Header.Get("X-Event-Key")
	if event == "" {
		webhooks.DefaultLog.Error("Missing X-Event-Key Header")
		http.Error(w, "400 Bad Request - Missing X-Event-Key Header", http.StatusBadRequest)
		return
	}
	webhooks.DefaultLog.Debug(fmt.Sprintf("X-Event-Key:%s", event))

	bitbucketEvent := Event(event)

	fn, ok := hook.eventFuncs[bitbucketEvent]
	// if no event registered
	if !ok {
		webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in bitbucket that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
		return
	}

	payload, err := ioutil.ReadAll(r.Body)
	if err != nil || len(payload) == 0 {
		webhooks.DefaultLog.Error("Issue reading Payload")
		http.Error(w, "Issue reading Payload", http.StatusInternalServerError)
		return
	}
	webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
	hd := webhooks.Header(r.Header)

	switch bitbucketEvent {
	case RepoPushEvent:
		var pl RepoPushPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoForkEvent:
		var pl RepoForkPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoUpdatedEvent:
		var pl RepoUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoCommitCommentCreatedEvent:
		var pl RepoCommitCommentCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoCommitStatusCreatedEvent:
		var pl RepoCommitStatusCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoCommitStatusUpdatedEvent:
		var pl RepoCommitStatusUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case IssueCreatedEvent:
		var pl IssueCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case IssueUpdatedEvent:
		var pl IssueUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case IssueCommentCreatedEvent:
		var pl IssueCommentCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCreatedEvent:
		var pl PullRequestCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestUpdatedEvent:
		var pl PullRequestUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestApprovedEvent:
		var pl PullRequestApprovedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestUnapprovedEvent:
		var pl PullRequestUnapprovedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestMergedEvent:
		var pl PullRequestMergedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestDeclinedEvent:
		var pl PullRequestDeclinedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCommentCreatedEvent:
		var pl PullRequestCommentCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCommentUpdatedEvent:
		var pl PullRequestCommentUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCommentDeletedEvent:
		var pl PullRequestCommentDeletedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	}
}

func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
	go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
		fn(results, header)
	}(fn, results, header)
}
<commit_msg>fix(bitbucket) typo in Event<commit_after>package bitbucket

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

	"gopkg.in/go-playground/webhooks.v3"
)

// Webhook instance contains all methods needed to process events
type Webhook struct {
	provider   webhooks.Provider
	uuid       string
	eventFuncs map[Event]webhooks.ProcessPayloadFunc
}

// Config defines the configuration to create a new Bitbucket Webhook instance
type Config struct {
	UUID string
}

// Event defines a Bitbucket hook event type
type Event string

// Bitbucket hook types
const (
	RepoPushEvent                  Event = "repo:push"
	RepoForkEvent                  Event = "repo:fork"
	RepoUpdatedEvent               Event = "repo:updated"
	RepoCommitCommentCreatedEvent  Event = "repo:commit_comment_created"
	RepoCommitStatusCreatedEvent   Event = "repo:commit_status_created"
	RepoCommitStatusUpdatedEvent   Event = "repo:commit_status_updated"
	IssueCreatedEvent              Event = "issue:created"
	IssueUpdatedEvent              Event = "issue:updated"
	IssueCommentCreatedEvent       Event = "issue:comment_created"
	PullRequestCreatedEvent        Event = "pullrequest:created"
	PullRequestUpdatedEvent        Event = "pullrequest:updated"
	PullRequestApprovedEvent       Event = "pullrequest:approved"
	PullRequestUnapprovedEvent     Event = "pullrequest:unapproved"
	PullRequestMergedEvent         Event = "pullrequest:fulfilled"
	PullRequestDeclinedEvent       Event = "pullrequest:rejected"
	PullRequestCommentCreatedEvent Event = "pullrequest:comment_created"
	PullRequestCommentUpdatedEvent Event = "pullrequest:comment_updated"
	PullRequestCommentDeletedEvent Event = "pullrequest:comment_deleted"
)

// New creates and returns a WebHook instance denoted by the Provider type
func New(config *Config) *Webhook {
	return &Webhook{
		provider:   webhooks.Bitbucket,
		uuid:       config.UUID,
		eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
	}
}

// Provider returns the current hooks provider ID
func (hook Webhook) Provider() webhooks.Provider {
	return hook.provider
}

// RegisterEvents registers the function to call when the specified event(s) are encountered
func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {

	for _, event := range events {
		hook.eventFuncs[event] = fn
	}
}

// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
	webhooks.DefaultLog.Info("Parsing Payload...")

	uuid := r.Header.Get("X-Hook-UUID")
	if uuid == "" {
		webhooks.DefaultLog.Error("Missing X-Hook-UUID Header")
		http.Error(w, "400 Bad Request - Missing X-Hook-UUID Header", http.StatusBadRequest)
		return
	}
	webhooks.DefaultLog.Debug(fmt.Sprintf("X-Hook-UUID:%s", uuid))

	if len(hook.uuid) > 0 {
		if uuid != hook.uuid {
			webhooks.DefaultLog.Error(fmt.Sprintf("X-Hook-UUID %s does not match configured uuid of %s", uuid, hook.uuid))
			http.Error(w, "403 Forbidden - X-Hook-UUID does not match", http.StatusForbidden)
			return
		}
	} else {
		webhooks.DefaultLog.Debug("hook uuid not defined - recommend setting for improved security")
	}

	event := r.Header.Get("X-Event-Key")
	if event == "" {
		webhooks.DefaultLog.Error("Missing X-Event-Key Header")
		http.Error(w, "400 Bad Request - Missing X-Event-Key Header", http.StatusBadRequest)
		return
	}
	webhooks.DefaultLog.Debug(fmt.Sprintf("X-Event-Key:%s", event))

	bitbucketEvent := Event(event)

	fn, ok := hook.eventFuncs[bitbucketEvent]
	// if no event registered
	if !ok {
		webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in bitbucket that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
		return
	}

	payload, err := ioutil.ReadAll(r.Body)
	if err != nil || len(payload) == 0 {
		webhooks.DefaultLog.Error("Issue reading Payload")
		http.Error(w, "Issue reading Payload", http.StatusInternalServerError)
		return
	}
	webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
	hd := webhooks.Header(r.Header)

	switch bitbucketEvent {
	case RepoPushEvent:
		var pl RepoPushPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoForkEvent:
		var pl RepoForkPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoUpdatedEvent:
		var pl RepoUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoCommitCommentCreatedEvent:
		var pl RepoCommitCommentCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoCommitStatusCreatedEvent:
		var pl RepoCommitStatusCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case RepoCommitStatusUpdatedEvent:
		var pl RepoCommitStatusUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case IssueCreatedEvent:
		var pl IssueCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case IssueUpdatedEvent:
		var pl IssueUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case IssueCommentCreatedEvent:
		var pl IssueCommentCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCreatedEvent:
		var pl PullRequestCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestUpdatedEvent:
		var pl PullRequestUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestApprovedEvent:
		var pl PullRequestApprovedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestUnapprovedEvent:
		var pl PullRequestUnapprovedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestMergedEvent:
		var pl PullRequestMergedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestDeclinedEvent:
		var pl PullRequestDeclinedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCommentCreatedEvent:
		var pl PullRequestCommentCreatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCommentUpdatedEvent:
		var pl PullRequestCommentUpdatedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	case PullRequestCommentDeletedEvent:
		var pl PullRequestCommentDeletedPayload
		json.Unmarshal([]byte(payload), &pl)
		hook.runProcessPayloadFunc(fn, pl, hd)
	}
}

func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
	go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
		fn(results, header)
	}(fn, results, header)
}
<|endoftext|>


<commit_before>package wiseman

import (
	"github.com/chzyer/adrs/customer"
	"github.com/chzyer/adrs/dns"
	"github.com/chzyer/adrs/mailman"
	"github.com/chzyer/adrs/utils"
	"github.com/chzyer/adrs/wiki"
	"gopkg.in/logex.v1"
)

type WiseMan struct {
	frontDoor   customer.Corridor
	backDoor    customer.Corridor
	mailMan     *mailman.MailMan
	book        *wiki.Wiki
	incomingBox chan *mailman.Envelope
	outgoingBox chan *mailman.Envelope
}

func NewWiseMan(frontDoor, backDoor customer.Corridor, book *wiki.Wiki, incomingBox, outgoingBox chan *mailman.Envelope) (*WiseMan, error) {
	w := &WiseMan{
		frontDoor:   frontDoor,
		backDoor:    backDoor,
		book:        book,
		incomingBox: incomingBox,
		outgoingBox: outgoingBox,
	}
	return w, nil
}

func (w *WiseMan) ServeAll() {
	var customer *customer.Customer
	var envelope *mailman.Envelope
	var err error
	for {
		select {
		case envelope = <-w.incomingBox:
			// new mail is receive
			err = w.receiveMail(envelope)
			if err != nil {
				logex.Error(err)
				envelope.Customer.LetItGo()
				continue
			}
			w.book.WriteDown(envelope.Customer.Msg, envelope.Customer.Raw)
			// say goodbye
			w.backDoor <- envelope.Customer
		case customer = <-w.frontDoor:
			// new customer is comming
			err = w.serve(customer)
			if err != nil {
				// oops!, the wise man is passed out!
				logex.Error(err)
				customer.LetItGo()
				continue
			}
		}
	}
}

func (w *WiseMan) serve(c *customer.Customer) error {
	r := utils.NewRecordReader(c.Raw)
	msg, err := dns.NewDNSMessage(r)
	if err != nil {
		return logex.Trace(err)
	}

	logex.Info("here comes a customer")
	// looking up the wikis.
	b, ok := w.book.Lookup(msg)
	if ok {
		copy(b.Block[:2], c.Raw.Block[:2])
		c.Raw = b
		c.Msg, err = dns.NewDNSMessage(utils.NewRecordReader(b))
		if err != nil {
			return logex.Trace(err, "oo")
		}
		w.backDoor <- c
		return nil
	}
	// ask others

	// we don't know where to send yet
	mail := w.writeMail(c, msg)

	logex.Info("sending a mail to others")

	// but don't worry, my mail man know
	w.sendMail(mail, c)
	return nil
}

func (w *WiseMan) sendMail(mail *mailman.Mail, c *customer.Customer) {
	w.outgoingBox <- &mailman.Envelope{mail, c}
}

func (w *WiseMan) receiveMail(e *mailman.Envelope) error {
	if e.Mail.Reply == nil {
		return logex.NewError("oops!")
	}

	logex.Info("we got a answer from remote")

	// write to wiki in case someone ask the same question
	e.Customer.SetRaw(e.Mail.Reply)
	msg, err := w.readMailAndDestory(e.Mail)
	if err != nil {
		return logex.Trace(err)
	}
	e.Customer.Msg = msg
	return nil
}

func (w *WiseMan) writeMail(c *customer.Customer, msg *dns.DNSMessage) *mailman.Mail {
	return &mailman.Mail{
		From:    c.Session,
		Content: msg,
	}
}

func (w *WiseMan) readMailAndDestory(m *mailman.Mail) (*dns.DNSMessage, error) {
	msg, err := dns.NewDNSMessage(utils.NewRecordReader(m.Reply))
	if err != nil {
		return nil, logex.Trace(err)
	}
	return msg, nil
}
<commit_msg>add debug<commit_after>package wiseman

import (
	"github.com/chzyer/adrs/customer"
	"github.com/chzyer/adrs/dns"
	"github.com/chzyer/adrs/mailman"
	"github.com/chzyer/adrs/utils"
	"github.com/chzyer/adrs/wiki"
	"gopkg.in/logex.v1"
)

type WiseMan struct {
	frontDoor   customer.Corridor
	backDoor    customer.Corridor
	mailMan     *mailman.MailMan
	book        *wiki.Wiki
	incomingBox chan *mailman.Envelope
	outgoingBox chan *mailman.Envelope
}

func NewWiseMan(frontDoor, backDoor customer.Corridor, book *wiki.Wiki, incomingBox, outgoingBox chan *mailman.Envelope) (*WiseMan, error) {
	w := &WiseMan{
		frontDoor:   frontDoor,
		backDoor:    backDoor,
		book:        book,
		incomingBox: incomingBox,
		outgoingBox: outgoingBox,
	}
	return w, nil
}

func (w *WiseMan) ServeAll() {
	var customer *customer.Customer
	var envelope *mailman.Envelope
	var err error
	for {
		select {
		case envelope = <-w.incomingBox:
			// new mail is receive
			err = w.receiveMail(envelope)
			if err != nil {
				logex.Error(err)
				envelope.Customer.LetItGo()
				continue
			}
			w.book.WriteDown(envelope.Customer.Msg, envelope.Customer.Raw)
			// say goodbye
			w.backDoor <- envelope.Customer
		case customer = <-w.frontDoor:
			// new customer is comming
			err = w.serve(customer)
			if err != nil {
				// oops!, the wise man is passed out!
				logex.Error(err)
				customer.LetItGo()
				continue
			}
		}
	}
}

func (w *WiseMan) serve(c *customer.Customer) error {
	r := utils.NewRecordReader(c.Raw)
	msg, err := dns.NewDNSMessage(r)
	if err != nil {
		return logex.Trace(err)
	}

	logex.Info("here comes a customer")
	// looking up the wikis.
	b, ok := w.book.Lookup(msg)
	if ok {
		copy(b.Block[:2], c.Raw.Block[:2])
		c.Raw = b
		c.Msg, err = dns.NewDNSMessage(utils.NewRecordReader(b))
		if err != nil {
			return logex.Trace(err, "oo")
		}
		for _, r := range c.Msg.Resources {
			if r.Type == dns.QTYPE_A {
				logex.Info(c.Msg.GetQueryAddr(), "->", r.RData)
			}
		}
		w.backDoor <- c
		return nil
	}
	// ask others

	// we don't know where to send yet
	mail := w.writeMail(c, msg)

	logex.Info("sending a mail to others")

	// but don't worry, my mail man know
	w.sendMail(mail, c)
	return nil
}

func (w *WiseMan) sendMail(mail *mailman.Mail, c *customer.Customer) {
	w.outgoingBox <- &mailman.Envelope{mail, c}
}

func (w *WiseMan) receiveMail(e *mailman.Envelope) error {
	if e.Mail.Reply == nil {
		return logex.NewError("oops!")
	}

	logex.Info("we got a answer from remote")

	// write to wiki in case someone ask the same question
	e.Customer.SetRaw(e.Mail.Reply)
	msg, err := w.readMailAndDestory(e.Mail)
	if err != nil {
		return logex.Trace(err)
	}
	e.Customer.Msg = msg
	return nil
}

func (w *WiseMan) writeMail(c *customer.Customer, msg *dns.DNSMessage) *mailman.Mail {
	return &mailman.Mail{
		From:    c.Session,
		Content: msg,
	}
}

func (w *WiseMan) readMailAndDestory(m *mailman.Mail) (*dns.DNSMessage, error) {
	msg, err := dns.NewDNSMessage(utils.NewRecordReader(m.Reply))
	if err != nil {
		return nil, logex.Trace(err)
	}
	return msg, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/subutai-io/base/agent/d2s/parser"
	"github.com/subutai-io/base/agent/lib/template"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

func main() {
	tmpdir := "/home/ubuntu/tmpfs/"

	if len(os.Args) <= 1 {
		fmt.Println("Please specify path to Dockerfile")
		os.Exit(1)
	}
	dockerfile := os.Args[1]
	if !strings.HasSuffix(strings.ToUpper(dockerfile), "DOCKERFILE") {
		dockerfile = dockerfile + "/Dockerfile"
	}

	head := `#!/bin/bash
mkdir -p /opt/docker2subutai/
cd /opt/docker2subutai/
uudecode $0 |tar zxf -
. .env
`

	//parse
	out, env, cmd, _ := parser.Parce(dockerfile)

	// if img != "" {
	// 	cmd := exec.Command("subutai", "import", img)
	// 	cmd.Stdout = os.Stdout
	// 	cmd.Stderr = os.Stderr
	// 	cmd.Run()
	// 	// TODO add exec check here.
	// }

	// create cmd file
	out = out + `
#create cmd file
cat > /opt/docker2subutai/cmd <<- EndOfCMD
#!/bin/bash
cd /opt/docker2subutai/
. .env
` + cmd + `
EndOfCMD

chmod a+x /opt/docker2subutai/cmd
`

	// !! move system to runlvl 1 and amke services
	out = out + `
if [ -f "/etc/systemd/system/default.target" ]; then
	mv /etc/systemd/system/default.target /etc/systemd/system/default.target.orig
	ln -s /lib/systemd/system/rescue.target /etc/systemd/system/default.target
fi

if [ -f "/etc/init/rc-sysinit.conf" ]; then
	sed -i 's/env DEFAULT_RUNLEVEL=2/env DEFAULT_RUNLEVEL=1/g' /etc/init/rc-sysinit.conf
fi

#create systemd service
mkdir -p /etc/systemd/system/
cat > /etc/systemd/system/docker2subutai.service <<- EndOfSystemD
[Unit]
Description=docker2subutai Service
After=rescue.target

[Service]
User=root
Group=root
ExecStart=/opt/docker2subutai/cmd
Restart=always

[Install]
WantedBy=rescue.target
EndOfSystemD

mkdir /etc/systemd/system/rescue.target.wants
ln -s /etc/systemd/system/docker2subutai.service /etc/systemd/system/rescue.target.wants/docker2subutai.service
ln -s /lib/systemd/system/ssh.service /etc/systemd/system/rescue.target.wants/

#create upstart service
cat > /etc/init/docker2subutai.conf <<- EndOfUpstart
description     "docker2subutai service"

start on stopped rc RUNLEVEL=[1]
stop on runlevel [!1]

respawn

exec /opt/docker2subutai/cmd
EndOfUpstart
if [ -f "/etc/init/ssh.conf" ]; then
	sed -i 's/start on runlevel \[2/start on runlevel \[12/g' /etc/init/ssh.conf
fi
`

	// create .env
	ioutil.WriteFile(strings.Trim(dockerfile, "Dockerfile")+".env", []byte(env), 0644)

	// make arch
	template.Tar(strings.Trim(dockerfile, "Dockerfile"), tmpdir+"archive.tar.gz")
	out = head + out + "\nexit 0\n\n"

	// compress arch into script
	data, _ := exec.Command("uuencode", tmpdir+"archive.tar.gz", "-").Output()
	out = out + string(data) // add archived data

	// write script
	// ioutil.WriteFile(strings.Trim(dockerfile, "Dockerfile")+"install.sh", []byte(out), 0755)
	ioutil.WriteFile(tmpdir+"install.sh", []byte(out), 0755)

	// clean
	_ = os.Remove(tmpdir + "archive.tar.gz")

}
<commit_msg>d2s / changed files archive method #156 disabled all-in-one install.sh all files compressed to archive.tar.gz<commit_after>package main

import (
	"fmt"
	"github.com/subutai-io/base/agent/d2s/parser"
	"github.com/subutai-io/base/agent/lib/template"
	"io/ioutil"
	"os"
	"strings"
)

func main() {
	tmpdir := "/home/ubuntu/tmpfs/"

	if len(os.Args) <= 1 {
		fmt.Println("Please specify path to Dockerfile")
		os.Exit(1)
	}
	dockerfile := os.Args[1]
	if !strings.HasSuffix(strings.ToUpper(dockerfile), "DOCKERFILE") {
		dockerfile = dockerfile + "/Dockerfile"
	}

	head := `#!/bin/bash
#mkdir -p /opt/docker2subutai/
cd /opt/docker2subutai/
#uudecode $0 |tar zxf -
. .env
`

	//parse
	out, env, cmd, _ := parser.Parce(dockerfile)

	// if img != "" {
	// 	cmd := exec.Command("subutai", "import", img)
	// 	cmd.Stdout = os.Stdout
	// 	cmd.Stderr = os.Stderr
	// 	cmd.Run()
	// 	// TODO add exec check here.
	// }

	// create cmd file
	out = out + `
#create cmd file
cat > /opt/docker2subutai/cmd <<- EndOfCMD
#!/bin/bash
cd /opt/docker2subutai/
. .env
` + cmd + `
EndOfCMD

chmod a+x /opt/docker2subutai/cmd
`

	// !! move system to runlvl 1 and amke services
	out = out + `
if [ -f "/etc/systemd/system/default.target" ]; then
	mv /etc/systemd/system/default.target /etc/systemd/system/default.target.orig
	ln -s /lib/systemd/system/rescue.target /etc/systemd/system/default.target
fi

if [ -f "/etc/init/rc-sysinit.conf" ]; then
	sed -i 's/env DEFAULT_RUNLEVEL=2/env DEFAULT_RUNLEVEL=1/g' /etc/init/rc-sysinit.conf
fi

#create systemd service
mkdir -p /etc/systemd/system/
cat > /etc/systemd/system/docker2subutai.service <<- EndOfSystemD
[Unit]
Description=docker2subutai Service
After=rescue.target

[Service]
User=root
Group=root
ExecStart=/opt/docker2subutai/cmd
Restart=always

[Install]
WantedBy=rescue.target
EndOfSystemD

mkdir /etc/systemd/system/rescue.target.wants
ln -s /etc/systemd/system/docker2subutai.service /etc/systemd/system/rescue.target.wants/docker2subutai.service
ln -s /lib/systemd/system/ssh.service /etc/systemd/system/rescue.target.wants/

#create upstart service
cat > /etc/init/docker2subutai.conf <<- EndOfUpstart
description     "docker2subutai service"

start on stopped rc RUNLEVEL=[1]
stop on runlevel [!1]

respawn

exec /opt/docker2subutai/cmd
EndOfUpstart
if [ -f "/etc/init/ssh.conf" ]; then
	sed -i 's/start on runlevel \[2/start on runlevel \[12/g' /etc/init/ssh.conf
fi
`
	out = head + out + "\nexit 0\n\n"
	// create .env
	ioutil.WriteFile(strings.Trim(dockerfile, "Dockerfile")+".env", []byte(env), 0644)

	// compress arch into script
	// data, _ := exec.Command("uuencode", tmpdir+"archive.tar.gz", "-").Output()
	// out = out + string(data) // add archived data

	// write script
	ioutil.WriteFile(strings.Trim(dockerfile, "Dockerfile")+"install.sh", []byte(out), 0755)

	// make arch
	template.Tar(strings.Trim(dockerfile, "Dockerfile"), tmpdir+"archive.tar.gz")

	// clean
	// _ = os.Remove(tmpdir + "archive.tar.gz")

}
<|endoftext|>


<commit_before>package sqlr

// tests for statement error conditions

import (
	"context"
	"database/sql"
	"errors"
	"testing"
)

type FakeDB struct {
	execErr         error
	rowsAffected    int64
	rowsAffectedErr error
	lastInsertId    int64
	lastInsertIdErr error
	queryErr        error
}

func (db *FakeDB) Exec(query string, args ...interface{}) (sql.Result, error) {
	if db.execErr != nil {
		return nil, db.execErr
	}
	return db, nil
}

func (db *FakeDB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
	return db.Exec(query, args...)
}

func (db *FakeDB) LastInsertId() (int64, error) {
	return db.lastInsertId, db.lastInsertIdErr
}

func (db *FakeDB) RowsAffected() (int64, error) {
	return db.rowsAffected, db.rowsAffectedErr
}

func (db *FakeDB) Query(query string, args ...interface{}) (*sql.Rows, error) {
	return nil, db.queryErr
}

func (db *FakeDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
	return db.Query(query, args...)
}

func TestSelectStmt1Errors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	tests := []struct {
		row        interface{}
		sql        string
		errPrepare string
		errExec    string
	}{
		{
			row:     Row{},
			sql:     "tablename",
			errExec: "expected rows to be *[]github.com/jjeffery/sqlr.Row, *[]*github.com/jjeffery/sqlr.Row, or *github.com/jjeffery/sqlr.Row",
		},
		{
			row:     nil,
			sql:     "tablename",
			errExec: "nil pointer",
		},
		{
			row:     (*Row)(nil),
			sql:     "tablename",
			errExec: "nil pointer",
		},
		{
			row:     &NotARow{},
			sql:     "tablename",
			errExec: "expected rows to be *[]github.com/jjeffery/sqlr.Row, *[]*github.com/jjeffery/sqlr.Row, or *github.com/jjeffery/sqlr.Row",
		},
		{
			row:        Row{},
			sql:        "select {} from {} where {}",
			errPrepare: `cannot expand "{}" in "select from" clause`,
		},
		{
			row:        Row{},
			sql:        "select {dodgy!} from xx where {}",
			errPrepare: `cannot expand "dodgy!" in "select columns" clause: unrecognised input near "!"`,
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %v", i, err)
			}
		} else {
			if err == nil {
				t.Errorf("%d: expected %q, got nil", i, tt.errPrepare)
			} else if err.Error() != tt.errPrepare {
				t.Errorf("%d: expected %q, got %v", i, tt.errPrepare, err)
			}
		}
		db := &FakeDB{}

		if err == nil {
			_, err = stmt.Select(ctx, db, tt.row)
			if err == nil || err.Error() != tt.errExec {
				t.Errorf("test case %d:\nwant=%q\ngot=%q", i, tt.errExec, err)
			}
		}
	}
}

func TestSelectStmt2Errors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	const errorTypePtr = "expected rows to be " +
		"*[]github.com/jjeffery/sqlr.Row, " +
		"*[]*github.com/jjeffery/sqlr.Row, or " +
		"*github.com/jjeffery/sqlr.Row"
	var invalidSlice []NotARow
	var validRows []Row
	tests := []struct {
		dest       interface{}
		sql        string
		args       []interface{}
		queryErr   error
		errText    string
		errPrepare string
	}{
		{
			dest:    []Row{},
			errText: errorTypePtr,
		},
		{
			dest:    make([]Row, 0),
			errText: errorTypePtr,
		},
		{
			dest:    nil,
			errText: "nil pointer",
		},
		{
			dest:    (*Row)(nil),
			errText: "nil pointer",
		},
		{
			dest:    &NotARow{},
			errText: errorTypePtr,
		},
		{
			dest:    &invalidSlice,
			errText: errorTypePtr,
		},
		{
			dest:       &validRows,
			sql:        "select {} from table {} where {}",
			errPrepare: `cannot expand "{}" in "select from" clause`,
			args:       []interface{}{},
		},
		{
			dest:     &validRows,
			sql:      "select {} from table where name=?",
			queryErr: errors.New("test query error"),
			errText:  `test query error`,
			args:     []interface{}{"somevalue"},
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %q", i, err)
			}
		} else {
			if err == nil {
				t.Errorf("%d: expected %q, got no error", i, tt.errPrepare)
			} else if err.Error() != tt.errPrepare {
				t.Errorf("%d:\nexpected %q,\ngot %q", i, tt.errPrepare, err)
			}
		}
		if err != nil {
			continue
		}

		db := &FakeDB{queryErr: tt.queryErr}

		_, err = stmt.Select(ctx, db, tt.dest, tt.args...)
		if err == nil || err.Error() != tt.errText {
			t.Errorf("%d: want=%q\ngot=%q", i, tt.errText, err)
		}
	}
}

func TestInsertRowStmtErrors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	tests := []struct {
		sql             string
		row             interface{}
		execErr         error
		lastInsertIdErr error
		errPrepare      string
		errText         string
	}{
		{
			sql:     "insert into tablename({}) values({})",
			row:     Row{},
			errText: "cannot set auto-increment value for type Row",
		},
		{
			sql:     "insert into tablename({}) values({})",
			row:     &Row{},
			execErr: errors.New("test error condition"),
			errText: "test error condition",
		},
		{
			sql:             "insert into tablename({}) values({})",
			row:             &Row{},
			lastInsertIdErr: errors.New("test LastInsertId"),
			errText:         "test LastInsertId",
		},
		{
			sql:        "insert into table values {}",
			row:        &Row{},
			errPrepare: `cannot expand "insert values" clause because "insert columns" clause is missing`,
		},
		{
			sql:        "insert into table({}) values({all})",
			row:        &Row{},
			errPrepare: `columns for "insert values" clause must match the "insert columns" clause`,
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %q", i, err)
			}
		} else if err == nil {
			t.Errorf("%d: expected %q, got nil", i, tt.errPrepare)
		} else if got, want := err.Error(), tt.errPrepare; got != want {
			t.Errorf("%d: expected %q, got %q", i, want, got)
		}

		if err != nil {
			continue
		}

		db := &FakeDB{
			execErr:         tt.execErr,
			lastInsertIdErr: tt.lastInsertIdErr,
		}

		_, err = stmt.Exec(ctx, db, tt.row)
		if err == nil || err.Error() != tt.errText {
			t.Errorf("expected=%q, actual=%v", tt.errText, err)
		}

	}
}

func TestExecRowStmtErrors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	tests := []struct {
		sql             string
		row             interface{}
		execErr         error
		rowsAffectedErr error
		errPrepare      string
		errText         string
	}{
		{
			sql:     "update tablename set {} where {}",
			row:     &Row{},
			execErr: errors.New("test error condition"),
			errText: "test error condition",
		},
		{
			sql:             "delete from tablename where {}",
			row:             &Row{},
			rowsAffectedErr: errors.New("test RowsAffected"),
			errText:         "test RowsAffected",
		},
		{
			sql:        "update table {}",
			row:        &Row{},
			errPrepare: `cannot expand "{}" in "update table" clause`,
		},
		{
			sql:     "select {} from tablename where {}",
			row:     &Row{},
			errText: `attempt to call Exec on select statement`,
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(&Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %q", i, err)
			}
		} else if err == nil {
			t.Errorf("%d: expected %q, got nil", i, tt.errPrepare)
		} else if got, want := err.Error(), tt.errPrepare; got != want {
			t.Errorf("%d: expected %q, got %q", i, want, got)
		}

		if err != nil {
			continue
		}

		db := &FakeDB{
			execErr:         tt.execErr,
			rowsAffectedErr: tt.rowsAffectedErr,
		}

		_, err = stmt.Exec(ctx, db, tt.row)
		if err == nil || err.Error() != tt.errText {
			t.Errorf("%d: expected=%q, actual=%q", i, tt.errText, err)
		}
	}
}

func TestInvalidStmts(t *testing.T) {
	type Row struct {
		ID     int64 `sql:"primary key"`
		Name   string
		Number int
	}

	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	schema := NewSchema(ForDB(db))
	var row Row
	var notRow int

	tests := []struct {
		fn   func() (int, error)
		want string
	}{
		{
			fn:   func() (int, error) { return schema.Exec(db, &notRow, "insert into rows({}) values({})") },
			want: `expected row type to be a struct, found int`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "insert into xyz values({})") },
			want: `cannot expand "insert values" clause because "insert columns" clause is missing`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "insert into xyz({}) values({pk})") },
			want: `columns for "insert values" clause must match the "insert columns" clause`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "update {} this is not valid SQL") },
			want: `cannot expand "{}" in "update table" clause`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "update rows set {} where {} and number=?") },
			want: `expected arg count=1, actual=0`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "delete from rows where {}") },
			want: `no such table: rows`,
		},
		{
			fn:   func() (int, error) { return schema.Select(db, &row, "select {alias} from rows") },
			want: `cannot expand "alias" in "select columns" clause: missing ident after 'alias'`,
		},
		{
			fn:   func() (int, error) { return schema.Select(db, &row, "select {'col1} from rows") },
			want: `cannot expand "'col1" in "select columns" clause: unrecognised input near "'col1"`,
		},
		{
			fn:   func() (int, error) { return schema.Select(db, &notRow, "select {} from rows") },
			want: `expected row type to be a struct, found int`,
		},
	}

	for i, tt := range tests {
		_, err := tt.fn()
		if err != nil {
			if tt.want != err.Error() {
				t.Errorf("%d: want %s, got %v", i, tt.want, err.Error())
			}
			continue
		}
		t.Errorf("%d: want %s, got nil", i, tt.want)
	}
}

func TestInvalidPrepare(t *testing.T) {
	schema := NewSchema(WithDialect(ANSISQL))
	var notRow []int
	_, err := schema.Prepare(notRow, "select {} from rows")
	want := `expected row type to be a struct, found int`
	if err != nil {
		if want != err.Error() {
			t.Errorf("want %s, got %v", want, err)
		}
	} else {
		t.Errorf("want %s, got nil", want)
	}
}
<commit_msg>Fix failing test.<commit_after>package sqlr

// tests for statement error conditions

import (
	"context"
	"database/sql"
	"errors"
	"testing"
)

type FakeDB struct {
	execErr         error
	rowsAffected    int64
	rowsAffectedErr error
	lastInsertId    int64
	lastInsertIdErr error
	queryErr        error
}

func (db *FakeDB) Exec(query string, args ...interface{}) (sql.Result, error) {
	if db.execErr != nil {
		return nil, db.execErr
	}
	return db, nil
}

func (db *FakeDB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
	return db.Exec(query, args...)
}

func (db *FakeDB) LastInsertId() (int64, error) {
	return db.lastInsertId, db.lastInsertIdErr
}

func (db *FakeDB) RowsAffected() (int64, error) {
	return db.rowsAffected, db.rowsAffectedErr
}

func (db *FakeDB) Query(query string, args ...interface{}) (*sql.Rows, error) {
	return nil, db.queryErr
}

func (db *FakeDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
	return db.Query(query, args...)
}

func TestSelectStmt1Errors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	tests := []struct {
		row        interface{}
		sql        string
		errPrepare string
		errExec    string
	}{
		{
			row:     Row{},
			sql:     "tablename",
			errExec: "expected rows to be *[]github.com/jjeffery/sqlr.Row, *[]*github.com/jjeffery/sqlr.Row, or *github.com/jjeffery/sqlr.Row",
		},
		{
			row:     nil,
			sql:     "tablename",
			errExec: "nil pointer",
		},
		{
			row:     (*Row)(nil),
			sql:     "tablename",
			errExec: "nil pointer",
		},
		{
			row:     &NotARow{},
			sql:     "tablename",
			errExec: "expected rows to be *[]github.com/jjeffery/sqlr.Row, *[]*github.com/jjeffery/sqlr.Row, or *github.com/jjeffery/sqlr.Row",
		},
		{
			row:        Row{},
			sql:        "select {} from {} where {}",
			errPrepare: `cannot expand "{}" in "select from" clause`,
		},
		{
			row:        Row{},
			sql:        "select {dodgy¥} from xx where {}",
			errPrepare: `cannot expand "dodgy¥" in "select columns" clause: unrecognised input near "¥"`,
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %v", i, err)
			}
		} else {
			if err == nil {
				t.Errorf("%d: expected %q, got nil", i, tt.errPrepare)
			} else if err.Error() != tt.errPrepare {
				t.Errorf("%d: expected %q, got %v", i, tt.errPrepare, err)
			}
		}
		db := &FakeDB{}

		if err == nil {
			_, err = stmt.Select(ctx, db, tt.row)
			if err == nil || err.Error() != tt.errExec {
				t.Errorf("test case %d:\nwant=%q\ngot=%q", i, tt.errExec, err)
			}
		}
	}
}

func TestSelectStmt2Errors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	const errorTypePtr = "expected rows to be " +
		"*[]github.com/jjeffery/sqlr.Row, " +
		"*[]*github.com/jjeffery/sqlr.Row, or " +
		"*github.com/jjeffery/sqlr.Row"
	var invalidSlice []NotARow
	var validRows []Row
	tests := []struct {
		dest       interface{}
		sql        string
		args       []interface{}
		queryErr   error
		errText    string
		errPrepare string
	}{
		{
			dest:    []Row{},
			errText: errorTypePtr,
		},
		{
			dest:    make([]Row, 0),
			errText: errorTypePtr,
		},
		{
			dest:    nil,
			errText: "nil pointer",
		},
		{
			dest:    (*Row)(nil),
			errText: "nil pointer",
		},
		{
			dest:    &NotARow{},
			errText: errorTypePtr,
		},
		{
			dest:    &invalidSlice,
			errText: errorTypePtr,
		},
		{
			dest:       &validRows,
			sql:        "select {} from table {} where {}",
			errPrepare: `cannot expand "{}" in "select from" clause`,
			args:       []interface{}{},
		},
		{
			dest:     &validRows,
			sql:      "select {} from table where name=?",
			queryErr: errors.New("test query error"),
			errText:  `test query error`,
			args:     []interface{}{"somevalue"},
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %q", i, err)
			}
		} else {
			if err == nil {
				t.Errorf("%d: expected %q, got no error", i, tt.errPrepare)
			} else if err.Error() != tt.errPrepare {
				t.Errorf("%d:\nexpected %q,\ngot %q", i, tt.errPrepare, err)
			}
		}
		if err != nil {
			continue
		}

		db := &FakeDB{queryErr: tt.queryErr}

		_, err = stmt.Select(ctx, db, tt.dest, tt.args...)
		if err == nil || err.Error() != tt.errText {
			t.Errorf("%d: want=%q\ngot=%q", i, tt.errText, err)
		}
	}
}

func TestInsertRowStmtErrors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	tests := []struct {
		sql             string
		row             interface{}
		execErr         error
		lastInsertIdErr error
		errPrepare      string
		errText         string
	}{
		{
			sql:     "insert into tablename({}) values({})",
			row:     Row{},
			errText: "cannot set auto-increment value for type Row",
		},
		{
			sql:     "insert into tablename({}) values({})",
			row:     &Row{},
			execErr: errors.New("test error condition"),
			errText: "test error condition",
		},
		{
			sql:             "insert into tablename({}) values({})",
			row:             &Row{},
			lastInsertIdErr: errors.New("test LastInsertId"),
			errText:         "test LastInsertId",
		},
		{
			sql:        "insert into table values {}",
			row:        &Row{},
			errPrepare: `cannot expand "insert values" clause because "insert columns" clause is missing`,
		},
		{
			sql:        "insert into table({}) values({all})",
			row:        &Row{},
			errPrepare: `columns for "insert values" clause must match the "insert columns" clause`,
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %q", i, err)
			}
		} else if err == nil {
			t.Errorf("%d: expected %q, got nil", i, tt.errPrepare)
		} else if got, want := err.Error(), tt.errPrepare; got != want {
			t.Errorf("%d: expected %q, got %q", i, want, got)
		}

		if err != nil {
			continue
		}

		db := &FakeDB{
			execErr:         tt.execErr,
			lastInsertIdErr: tt.lastInsertIdErr,
		}

		_, err = stmt.Exec(ctx, db, tt.row)
		if err == nil || err.Error() != tt.errText {
			t.Errorf("expected=%q, actual=%v", tt.errText, err)
		}

	}
}

func TestExecRowStmtErrors(t *testing.T) {
	type Row struct {
		ID   int64 `sql:"primary key autoincrement"`
		Name string
	}
	type NotARow struct {
		ID            int `sql:"primary key autoincrement"`
		SomethingElse int
	}
	tests := []struct {
		sql             string
		row             interface{}
		execErr         error
		rowsAffectedErr error
		errPrepare      string
		errText         string
	}{
		{
			sql:     "update tablename set {} where {}",
			row:     &Row{},
			execErr: errors.New("test error condition"),
			errText: "test error condition",
		},
		{
			sql:             "delete from tablename where {}",
			row:             &Row{},
			rowsAffectedErr: errors.New("test RowsAffected"),
			errText:         "test RowsAffected",
		},
		{
			sql:        "update table {}",
			row:        &Row{},
			errPrepare: `cannot expand "{}" in "update table" clause`,
		},
		{
			sql:     "select {} from tablename where {}",
			row:     &Row{},
			errText: `attempt to call Exec on select statement`,
		},
	}

	for i, tt := range tests {
		schema := NewSchema()
		ctx := context.Background()
		stmt, err := schema.Prepare(&Row{}, tt.sql)
		if tt.errPrepare == "" {
			if err != nil {
				t.Errorf("%d: expected no error, got %q", i, err)
			}
		} else if err == nil {
			t.Errorf("%d: expected %q, got nil", i, tt.errPrepare)
		} else if got, want := err.Error(), tt.errPrepare; got != want {
			t.Errorf("%d: expected %q, got %q", i, want, got)
		}

		if err != nil {
			continue
		}

		db := &FakeDB{
			execErr:         tt.execErr,
			rowsAffectedErr: tt.rowsAffectedErr,
		}

		_, err = stmt.Exec(ctx, db, tt.row)
		if err == nil || err.Error() != tt.errText {
			t.Errorf("%d: expected=%q, actual=%q", i, tt.errText, err)
		}
	}
}

func TestInvalidStmts(t *testing.T) {
	type Row struct {
		ID     int64 `sql:"primary key"`
		Name   string
		Number int
	}

	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	schema := NewSchema(ForDB(db))
	var row Row
	var notRow int

	tests := []struct {
		fn   func() (int, error)
		want string
	}{
		{
			fn:   func() (int, error) { return schema.Exec(db, &notRow, "insert into rows({}) values({})") },
			want: `expected row type to be a struct, found int`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "insert into xyz values({})") },
			want: `cannot expand "insert values" clause because "insert columns" clause is missing`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "insert into xyz({}) values({pk})") },
			want: `columns for "insert values" clause must match the "insert columns" clause`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "update {} this is not valid SQL") },
			want: `cannot expand "{}" in "update table" clause`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "update rows set {} where {} and number=?") },
			want: `expected arg count=1, actual=0`,
		},
		{
			fn:   func() (int, error) { return schema.Exec(db, &row, "delete from rows where {}") },
			want: `no such table: rows`,
		},
		{
			fn:   func() (int, error) { return schema.Select(db, &row, "select {alias} from rows") },
			want: `cannot expand "alias" in "select columns" clause: missing ident after 'alias'`,
		},
		{
			fn:   func() (int, error) { return schema.Select(db, &row, "select {'col1} from rows") },
			want: `cannot expand "'col1" in "select columns" clause: unrecognised input near "'col1"`,
		},
		{
			fn:   func() (int, error) { return schema.Select(db, &notRow, "select {} from rows") },
			want: `expected row type to be a struct, found int`,
		},
	}

	for i, tt := range tests {
		_, err := tt.fn()
		if err != nil {
			if tt.want != err.Error() {
				t.Errorf("%d: want %s, got %v", i, tt.want, err.Error())
			}
			continue
		}
		t.Errorf("%d: want %s, got nil", i, tt.want)
	}
}

func TestInvalidPrepare(t *testing.T) {
	schema := NewSchema(WithDialect(ANSISQL))
	var notRow []int
	_, err := schema.Prepare(notRow, "select {} from rows")
	want := `expected row type to be a struct, found int`
	if err != nil {
		if want != err.Error() {
			t.Errorf("want %s, got %v", want, err)
		}
	} else {
		t.Errorf("want %s, got nil", want)
	}
}
<|endoftext|>


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

package storage

import (
	"fmt"
	"strconv"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

// MysqlRecord - standard record (struct) for mysql storage package
type MysqlRecord struct {
	Host     string
	Port     int
	Username string
	Password string
	DataBase string
	Table    string
}

// Search data in the storage
func (mysql *MysqlRecord) Search(name string, query string) (map[string][]string, error) {
	result, err := mysql.searchRaw(mysql.Table, name, query)
	if err != nil {
		return nil, err
	}
	if len(result) > 0 {
		return result[0], nil
	}

	data := make(map[string][]string) // empty result
	return data, nil
}

// SearchRelated - search data in the storage from related type or table
func (mysql *MysqlRecord) SearchRelated(
	typeTable string, name string, query string) (map[string][]string, error) {

	result, err := mysql.searchRaw(typeTable, name, query)
	if err != nil {
		return nil, err
	}
	if len(result) > 0 {
		return result[0], nil
	}

	data := make(map[string][]string)
	return data, nil
}

// SearchMultiple - search multiple records of data in the storage
func (mysql *MysqlRecord) SearchMultiple(
	typeTable string, name string, query string) (map[string][]string, error) {

	result, err := mysql.searchRaw(typeTable, name, query)
	if err != nil {
		return nil, err
	}

	data := make(map[string][]string)

	if len(result) > 0 {
		for _, item := range result {
			for key, value := range item {
				data[key] = append(data[key], value...)
			}
		}
		return data, nil
	}

	return data, nil
}

func (mysql *MysqlRecord) searchRaw(typeTable string, name string, query string) ([]map[string][]string, error) {
	// Thanks to https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes
	db, err := sql.Open("mysql",	mysql.Username + ":" + mysql.Password +
					"@tcp(" + mysql.Host + ":" + strconv.Itoa(mysql.Port) + ")/" +
					mysql.DataBase + "?charset=utf8")

	if err != nil {
		return nil, fmt.Errorf("Mysql connection error: %v", err)
	}
	defer db.Close()

	// Execute the query
	rows, err := db.Query("SELECT * FROM " + typeTable + " where " + name + "=?", query) // TODO: prevent sqli
	if err != nil {
		return nil, fmt.Errorf("Mysql query error: %v", err)
	}

	// Get column names
	columns, err := rows.Columns()
	if err != nil {
		return nil, fmt.Errorf("Mysql column name query error: %v", err)
	}

	// Make a slice for the values
	values := make([]sql.RawBytes, len(columns))

	// rows.Scan wants '[]interface{}' as an argument, so we must copy the
	// references into such a slice
	// See http://code.google.com/p/go-wiki/wiki/InterfaceSlice for details
	scanArgs := make([]interface{}, len(values))
	for i := range values {
		scanArgs[i] = &values[i]
	}

	// Fetch rows
	var data []map[string][]string
	for rows.Next() {
		// get RawBytes from data
		err = rows.Scan(scanArgs...)
		if err != nil {
			return nil, fmt.Errorf("Mysql scan error: %v", err)
		}

		// Now do something with the data.
		// Here we just print each column as a string.
		var value string
		element := make(map[string][]string)
		for i, col := range values {
			// Here we can check if the value is nil (NULL value)
			if col == nil {
				value = "n/a"
			} else {
				value = string(col)
			}
			element[columns[i]] = []string{value}
		}
		data = append(data, element)
	}

	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("Mysql row read error: %v", err)
	}

	return data, nil
}
<commit_msg>mysql storage filters input<commit_after>// Copyright 2015 Openprovider Authors. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

package storage

import (
	"fmt"
	"strconv"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

// MysqlRecord - standard record (struct) for mysql storage package
type MysqlRecord struct {
	Host     string
	Port     int
	Username string
	Password string
	DataBase string
	Table    string
}

// Search data in the storage
func (mysql *MysqlRecord) Search(name string, query string) (map[string][]string, error) {
	result, err := mysql.searchRaw(mysql.Table, name, query)
	if err != nil {
		return nil, err
	}
	if len(result) > 0 {
		return result[0], nil
	}

	data := make(map[string][]string) // empty result
	return data, nil
}

// SearchRelated - search data in the storage from related type or table
func (mysql *MysqlRecord) SearchRelated(
	typeTable string, name string, query string) (map[string][]string, error) {

	result, err := mysql.searchRaw(typeTable, name, query)
	if err != nil {
		return nil, err
	}
	if len(result) > 0 {
		return result[0], nil
	}

	data := make(map[string][]string)
	return data, nil
}

// SearchMultiple - search multiple records of data in the storage
func (mysql *MysqlRecord) SearchMultiple(
	typeTable string, name string, query string) (map[string][]string, error) {

	result, err := mysql.searchRaw(typeTable, name, query)
	if err != nil {
		return nil, err
	}

	data := make(map[string][]string)

	if len(result) > 0 {
		for _, item := range result {
			for key, value := range item {
				data[key] = append(data[key], value...)
			}
		}
		return data, nil
	}

	return data, nil
}

func (mysql *MysqlRecord) searchRaw(typeTable string, name string, query string) ([]map[string][]string, error) {
	// Thanks to https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes
	db, err := sql.Open("mysql",	mysql.Username + ":" + mysql.Password +
					"@tcp(" + mysql.Host + ":" + strconv.Itoa(mysql.Port) + ")/" +
					mysql.DataBase + "?charset=utf8")

	if err != nil {
		return nil, fmt.Errorf("Mysql connection error: %v", err)
	}

	// Filter input
	name = filterString(name, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")
	query = filterString(query, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-")
	// Execute the query
	rows, err := db.Query("SELECT * FROM " + typeTable + " where " + name + "=?", query)
	if err != nil {
		return nil, fmt.Errorf("Mysql query error: %v", err)
	}

	// Get column names
	columns, err := rows.Columns()
	if err != nil {
		return nil, fmt.Errorf("Mysql column name query error: %v", err)
	}

	// Make a slice for the values
	values := make([]sql.RawBytes, len(columns))

	// rows.Scan wants '[]interface{}' as an argument, so we must copy the
	// references into such a slice
	// See http://code.google.com/p/go-wiki/wiki/InterfaceSlice for details
	scanArgs := make([]interface{}, len(values))
	for i := range values {
		scanArgs[i] = &values[i]
	}

	// Fetch rows
	var data []map[string][]string
	for rows.Next() {
		// get RawBytes from data
		err = rows.Scan(scanArgs...)
		if err != nil {
			return nil, fmt.Errorf("Mysql scan error: %v", err)
		}

		// Now do something with the data.
		// Here we just print each column as a string.
		var value string
		element := make(map[string][]string)
		for i, col := range values {
			// Here we can check if the value is nil (NULL value)
			if col == nil {
				value = "n/a"
			} else {
				value = string(col)
			}
			element[columns[i]] = []string{value}
		}
		data = append(data, element)
	}

	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("Mysql row read error: %v", err)
	}

	return data, nil
}

func filterString(str, chr string) string {
	return strings.Map(func(r rune) rune {
		if strings.IndexRune(chr, r) > 0 {
			return r
		}
		return -1
	}, str)
}
<|endoftext|>


<commit_before>package common

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"time"

	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/version"
	"github.com/portworx/kvdb"
)

const (
	// DefaultRetryCount for etcd operations
	DefaultRetryCount = 60
	// DefaultIntervalBetweenRetries for etcd failed operations
	DefaultIntervalBetweenRetries = time.Millisecond * 500
	// Bootstrap key
	Bootstrap = "kvdb/bootstrap"
	// DefaultDialTimeout in etcd http requests
	// the maximum amount of time a dial will wait for a connection to setup.
	// 30s is long enough for most of the network conditions.
	DefaultDialTimeout = 30 * time.Second
	// DefaultLockTTL is the ttl for an etcd lock
	DefaultLockTTL = 16
	// DefaultLockRefreshDuration is the time interval for refreshing an etcd lock
	DefaultLockRefreshDuration = 2 * time.Second
)

// EtcdCommon defined the common functions between v2 and v3 etcd implementations.
type EtcdCommon interface {
	// GetAuthInfoFromOptions
	GetAuthInfoFromOptions() (transport.TLSInfo, string, string, error)

	// GetRetryCount
	GetRetryCount() int
}

// EtcdLock combines Mutex and channel
type EtcdLock struct {
	Done     chan struct{}
	Unlocked bool
	Err      error
	Tag      string
	sync.Mutex
}

// LockerIDInfo id of locker
type LockerIDInfo struct {
	LockerID string
}

type etcdCommon struct {
	options map[string]string
}

var (
	cmd *exec.Cmd
)

// NewEtcdCommon returns the EtcdCommon interface
func NewEtcdCommon(options map[string]string) EtcdCommon {
	return &etcdCommon{
		options: options,
	}
}

func (ec *etcdCommon) GetRetryCount() int {
	retryCount, ok := ec.options[kvdb.RetryCountKey]
	if !ok {
		return DefaultRetryCount
	}
	retry, err := strconv.ParseInt(retryCount, 10, 0)
	if err != nil {
		// use default value
		return DefaultRetryCount
	}
	return int(retry)
}

func (ec *etcdCommon) GetAuthInfoFromOptions() (transport.TLSInfo, string, string, error) {
	var (
		username       string
		password       string
		caFile         string
		certFile       string
		keyFile        string
		trustedCAFile  string
		clientCertAuth bool
		err            error
	)
	// options provided. Probably auth options
	if ec.options != nil || len(ec.options) > 0 {
		// Check if username provided
		username, _ = ec.options[kvdb.UsernameKey]
		// Check if password provided
		password, _ = ec.options[kvdb.PasswordKey]
		// Check if CA file provided
		caFile, _ = ec.options[kvdb.CAFileKey]
		// Check if certificate file provided
		certFile, _ = ec.options[kvdb.CertFileKey]
		// Check if certificate key is provided
		keyFile, _ = ec.options[kvdb.CertKeyFileKey]
		// Check if trusted ca file is provided
		trustedCAFile, _ = ec.options[kvdb.TrustedCAFileKey]
		// Check if client cert auth is provided
		clientCertAuthStr, ok := ec.options[kvdb.ClientCertAuthKey]
		if !ok {
			clientCertAuth = false
		} else {
			clientCertAuth, err = strconv.ParseBool(clientCertAuthStr)
			if err != nil {
				clientCertAuth = false
			}
		}
	}
	tls := transport.TLSInfo{
		CAFile:         caFile,
		CertFile:       certFile,
		KeyFile:        keyFile,
		TrustedCAFile:  trustedCAFile,
		ClientCertAuth: clientCertAuth,
	}
	return tls, username, password, nil
}

// Version returns the version of the provided etcd server
func Version(url string, options map[string]string) (string, error) {
	useTLS := false
	tlsConfig := &tls.Config{}
	// Check if CA file provided
	caFile, ok := options[kvdb.CAFileKey]
	if ok && caFile != "" {
		useTLS = true
		// Load CA cert
		caCert, err := ioutil.ReadFile(caFile)
		if err != nil {
			return "", err
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(caCert)
		tlsConfig.RootCAs = caCertPool
	}
	// Check if certificate file provided
	certFile, certOk := options[kvdb.CertFileKey]
	// Check if certificate key is provided
	keyFile, keyOk := options[kvdb.CertKeyFileKey]
	if certOk && keyOk && certFile != "" && keyFile != "" {
		useTLS = true
		// Load client cert
		cert, err := tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			return "", err
		}
		tlsConfig.Certificates = []tls.Certificate{cert}
	}

	var client *http.Client
	if useTLS {
		tlsConfig.BuildNameToCertificate()
		transport := &http.Transport{TLSClientConfig: tlsConfig}
		client = &http.Client{Transport: transport}
	} else {
		client = &http.Client{}
	}

	// Do GET something
	resp, err := client.Get(url + "/version")
	if err != nil {
		return "", fmt.Errorf("Error in obtaining etcd version: %v", err)
	}
	defer resp.Body.Close()

	// Dump response
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Error in obtaining etcd version: %v", err)
	}

	var version version.Versions
	err = json.Unmarshal(data, &version)
	if err != nil {
		// Probably a version less than 2.3. Default to using v2 apis
		return kvdb.EtcdBaseVersion, nil
	}
	if version.Server == "" {
		// This should never happen in an ideal scenario unless
		// etcd messes up. To avoid a crash further in this code
		// we return an error
		return "", fmt.Errorf("Unable to determine etcd version (empty response from etcd)")
	}
	if version.Server[0] == '2' || version.Server[0] == '1' {
		return kvdb.EtcdBaseVersion, nil
	} else if version.Server[0] == '3' {
		return kvdb.EtcdVersion3, nil
	} else {
		return "", fmt.Errorf("Unsupported etcd version: %v", version.Server)
	}
}

func TestStart() error {
	dataDir := "/tmp/etcd"
	os.RemoveAll(dataDir)
	cmd = exec.Command("etcd", "--advertise-client-urls", "http://127.0.0.1:2379", "--data-dir", dataDir)
	err := cmd.Start()
	time.Sleep(5 * time.Second)
	return err
}

func TestStop() error {
	return cmd.Process.Kill()
}
<commit_msg>PWX-5680: Fix etcd when using https and username:password but no certs<commit_after>package common

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"time"

	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/version"
	"github.com/portworx/kvdb"
)

const (
	// DefaultRetryCount for etcd operations
	DefaultRetryCount = 60
	// DefaultIntervalBetweenRetries for etcd failed operations
	DefaultIntervalBetweenRetries = time.Millisecond * 500
	// Bootstrap key
	Bootstrap = "kvdb/bootstrap"
	// DefaultDialTimeout in etcd http requests
	// the maximum amount of time a dial will wait for a connection to setup.
	// 30s is long enough for most of the network conditions.
	DefaultDialTimeout = 30 * time.Second
	// DefaultLockTTL is the ttl for an etcd lock
	DefaultLockTTL = 16
	// DefaultLockRefreshDuration is the time interval for refreshing an etcd lock
	DefaultLockRefreshDuration = 2 * time.Second
)

// EtcdCommon defined the common functions between v2 and v3 etcd implementations.
type EtcdCommon interface {
	// GetAuthInfoFromOptions
	GetAuthInfoFromOptions() (transport.TLSInfo, string, string, error)

	// GetRetryCount
	GetRetryCount() int
}

// EtcdLock combines Mutex and channel
type EtcdLock struct {
	Done     chan struct{}
	Unlocked bool
	Err      error
	Tag      string
	sync.Mutex
}

// LockerIDInfo id of locker
type LockerIDInfo struct {
	LockerID string
}

type etcdCommon struct {
	options map[string]string
}

var (
	cmd *exec.Cmd
)

// NewEtcdCommon returns the EtcdCommon interface
func NewEtcdCommon(options map[string]string) EtcdCommon {
	return &etcdCommon{
		options: options,
	}
}

func (ec *etcdCommon) GetRetryCount() int {
	retryCount, ok := ec.options[kvdb.RetryCountKey]
	if !ok {
		return DefaultRetryCount
	}
	retry, err := strconv.ParseInt(retryCount, 10, 0)
	if err != nil {
		// use default value
		return DefaultRetryCount
	}
	return int(retry)
}

func (ec *etcdCommon) GetAuthInfoFromOptions() (transport.TLSInfo, string, string, error) {
	var (
		username       string
		password       string
		caFile         string
		certFile       string
		keyFile        string
		trustedCAFile  string
		clientCertAuth bool
		err            error
	)
	// options provided. Probably auth options
	if ec.options != nil || len(ec.options) > 0 {
		// Check if username provided
		username, _ = ec.options[kvdb.UsernameKey]
		// Check if password provided
		password, _ = ec.options[kvdb.PasswordKey]
		// Check if CA file provided
		caFile, _ = ec.options[kvdb.CAFileKey]
		// Check if certificate file provided
		certFile, _ = ec.options[kvdb.CertFileKey]
		// Check if certificate key is provided
		keyFile, _ = ec.options[kvdb.CertKeyFileKey]
		// Check if trusted ca file is provided
		trustedCAFile, _ = ec.options[kvdb.TrustedCAFileKey]
		// Check if client cert auth is provided
		clientCertAuthStr, ok := ec.options[kvdb.ClientCertAuthKey]
		if !ok {
			clientCertAuth = false
		} else {
			clientCertAuth, err = strconv.ParseBool(clientCertAuthStr)
			if err != nil {
				clientCertAuth = false
			}
		}
	}
	tls := transport.TLSInfo{
		CAFile:         caFile,
		CertFile:       certFile,
		KeyFile:        keyFile,
		TrustedCAFile:  trustedCAFile,
		ClientCertAuth: clientCertAuth,
	}
	return tls, username, password, nil
}

// Version returns the version of the provided etcd server
func Version(uri string, options map[string]string) (string, error) {
	useTLS := false
	tlsConfig := &tls.Config{}
	// Check if CA file provided
	caFile, ok := options[kvdb.CAFileKey]
	if ok && caFile != "" {
		useTLS = true
		// Load CA cert
		caCert, err := ioutil.ReadFile(caFile)
		if err != nil {
			return "", err
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(caCert)
		tlsConfig.RootCAs = caCertPool
	}
	// Check if certificate file provided
	certFile, certOk := options[kvdb.CertFileKey]
	// Check if certificate key is provided
	keyFile, keyOk := options[kvdb.CertKeyFileKey]
	if certOk && keyOk && certFile != "" && keyFile != "" {
		useTLS = true
		// Load client cert
		cert, err := tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			return "", err
		}
		tlsConfig.Certificates = []tls.Certificate{cert}
	}

	var client *http.Client
	if useTLS {
		tlsConfig.BuildNameToCertificate()
		transport := &http.Transport{TLSClientConfig: tlsConfig}
		client = &http.Client{Transport: transport}
	} else {
		tempURL, _ := url.Parse(uri)
		if tempURL.Scheme == "https" {
			transport := &http.Transport{TLSClientConfig: &tls.Config{}}
			client = &http.Client{Transport: transport}
		} else {
			client = &http.Client{}
		}
	}

	// Do GET something
	resp, err := client.Get(uri + "/version")
	if err != nil {
		return "", fmt.Errorf("Error in obtaining etcd version: %v", err)
	}
	defer resp.Body.Close()

	// Dump response
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Error in obtaining etcd version: %v", err)
	}

	var version version.Versions
	err = json.Unmarshal(data, &version)
	if err != nil {
		// Probably a version less than 2.3. Default to using v2 apis
		return kvdb.EtcdBaseVersion, nil
	}
	if version.Server == "" {
		// This should never happen in an ideal scenario unless
		// etcd messes up. To avoid a crash further in this code
		// we return an error
		return "", fmt.Errorf("Unable to determine etcd version (empty response from etcd)")
	}
	if version.Server[0] == '2' || version.Server[0] == '1' {
		return kvdb.EtcdBaseVersion, nil
	} else if version.Server[0] == '3' {
		return kvdb.EtcdVersion3, nil
	} else {
		return "", fmt.Errorf("Unsupported etcd version: %v", version.Server)
	}
}

func TestStart() error {
	dataDir := "/tmp/etcd"
	os.RemoveAll(dataDir)
	cmd = exec.Command("etcd", "--advertise-client-urls", "http://127.0.0.1:2379", "--data-dir", dataDir)
	err := cmd.Start()
	time.Sleep(5 * time.Second)
	return err
}

func TestStop() error {
	return cmd.Process.Kill()
}
<|endoftext|>


<commit_before>package agent

// You can overridden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/agent.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "3.0-beta.16"
var buildVersion string = ""

func Version() string {
	return baseVersion
}

func BuildVersion() string {
	if buildVersion != "" {
		return buildVersion
	} else {
		return "x"
	}
}
<commit_msg>Bump version<commit_after>package agent

// You can overridden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/agent.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "3.0-beta.17"
var buildVersion string = ""

func Version() string {
	return baseVersion
}

func BuildVersion() string {
	if buildVersion != "" {
		return buildVersion
	} else {
		return "x"
	}
}
<|endoftext|>