<commit_before>package main

import (
	"bytes"
	"net/http"
	"net/http/cookiejar"
	"strconv"

	"fmt"
	"time"

	"regexp"

	"encoding/json"

	"io/ioutil"

	"os"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/mmcdole/gofeed"
)

type Anime struct {
	ID                     uint           `json:"id"`
	Name                   string         `gorm:"index" json:"name"`
	Thumbnail              string         `json:"thumbnail"`
	AutoDownloadResolution uint           `json:"auto_download_resolution"`
	AutoDownloadGroupID    uint           `json:"auto_download_group_id"`
	SubbingGroups          []SubbingGroup `json:"subbing_groups" gorm:"many2many:anime_subbing_groups;"`
	Episodes               []Episode      `json:"episodes"`
}

type Episode struct {
	ID       uint      `json:"id"`
	AnimeID  uint      `json:"anime_id"`
	Number   uint      `gorm:"index"`
	Torrents []Torrent `json:"torrents"`
}

type Torrent struct {
	ID             uint      `json:"id"`
	EpisodeID      uint      `json:"episode_id"`
	SubbingGroupID uint      `json:"subbing_group_id"`
	Link           string    `json:"link"`
	Extension      string    `json:"extension"`
	PubDate        time.Time `gorm:"index" json:"pub_date"`
	Resolution     uint      `json:"resolution"`
	Title          string    `json:"title"`
	Downloaded     bool      `gorm:"index" json:"downloaded"`
}

type SubbingGroup struct {
	ID   uint   `json:"id"`
	Name string `gorm:"index" json:"name"`
}

type GoogleSearchResult struct {
	Kind string `json:"kind"`
	URL  struct {
		Type     string `json:"type"`
		Template string `json:"template"`
	} `json:"url"`
	Queries struct {
		Request []struct {
			Title          string `json:"title"`
			TotalResults   string `json:"totalResults"`
			SearchTerms    string `json:"searchTerms"`
			Count          int    `json:"count"`
			StartIndex     int    `json:"startIndex"`
			InputEncoding  string `json:"inputEncoding"`
			OutputEncoding string `json:"outputEncoding"`
			Safe           string `json:"safe"`
			Cx             string `json:"cx"`
			SearchType     string `json:"searchType"`
			ImgSize        string `json:"imgSize"`
		} `json:"request"`
		NextPage []struct {
			Title          string `json:"title"`
			TotalResults   string `json:"totalResults"`
			SearchTerms    string `json:"searchTerms"`
			Count          int    `json:"count"`
			StartIndex     int    `json:"startIndex"`
			InputEncoding  string `json:"inputEncoding"`
			OutputEncoding string `json:"outputEncoding"`
			Safe           string `json:"safe"`
			Cx             string `json:"cx"`
			SearchType     string `json:"searchType"`
			ImgSize        string `json:"imgSize"`
		} `json:"nextPage"`
	} `json:"queries"`
	Context struct {
		Title string `json:"title"`
	} `json:"context"`
	SearchInformation struct {
		SearchTime            float64 `json:"searchTime"`
		FormattedSearchTime   string  `json:"formattedSearchTime"`
		TotalResults          string  `json:"totalResults"`
		FormattedTotalResults string  `json:"formattedTotalResults"`
	} `json:"searchInformation"`
	Items []struct {
		Kind        string `json:"kind"`
		Title       string `json:"title"`
		HTMLTitle   string `json:"htmlTitle"`
		Link        string `json:"link"`
		DisplayLink string `json:"displayLink"`
		Snippet     string `json:"snippet"`
		HTMLSnippet string `json:"htmlSnippet"`
		Mime        string `json:"mime"`
		Image       struct {
			ContextLink     string `json:"contextLink"`
			Height          int    `json:"height"`
			Width           int    `json:"width"`
			ByteSize        int    `json:"byteSize"`
			ThumbnailLink   string `json:"thumbnailLink"`
			ThumbnailHeight int    `json:"thumbnailHeight"`
			ThumbnailWidth  int    `json:"thumbnailWidth"`
		} `json:"image"`
	} `json:"items"`
}

var db *gorm.DB

type AutoDownloadRequest struct {
	Resolution     uint `json:"resolution"`
	SubbingGroupID uint `json:"subbing_group_id"`
}

func main() {
	var err error
	db, err = gorm.Open("sqlite3", "data/nyaatorrentler.db")
	if err != nil {
		panic("Failed to connect to database...")
	}
	// Migrate the schema
	db.AutoMigrate(&Anime{}, &Episode{}, &Torrent{}, &SubbingGroup{})

	initTicker()

	e := echo.New()
	e.Use(middleware.Logger())
	e.Static("/", "public")
	e.GET("/api/animes", func(c echo.Context) error {
		var anime []Anime
		//db.Joins("LEFT JOIN episodes ON episodes.anime_id = animes.ID").Joins("LEFT JOIN torrents ON torrents.episode_id = episodes.ID").Limit(100).Order("ID desc").Find(&anime)
		db.Preload("Episodes").Preload("SubbingGroups").Preload("Episodes.Torrents").Find(&anime)
		return c.JSON(http.StatusOK, anime)
	})

	e.POST("/api/animes/:id/toggle", func(c echo.Context) error {
		id, _ := strconv.Atoi(c.Param("id"))
		anime := Anime{ID: uint(id)}
		var autoDownloadRequest AutoDownloadRequest
		c.Bind(&autoDownloadRequest)
		db.First(&anime)
		if anime.AutoDownloadResolution == 0 {
			anime.AutoDownloadResolution = autoDownloadRequest.Resolution
			anime.AutoDownloadGroupID = autoDownloadRequest.SubbingGroupID
		} else {
			anime.AutoDownloadResolution = 0
			anime.AutoDownloadGroupID = 0
		}
		db.Save(anime)
		return c.JSON(http.StatusOK, &anime)
	})

	e.POST("/api/torrent/:id/download", func(c echo.Context) error {
		var torrent Torrent
		db.First(&torrent, c.Param("id"))
		download(torrent)
		return c.JSON(http.StatusOK, torrent)
	})

	e.Logger.Fatal(e.Start(":1323"))
}

func UpdateThumbnail(a *Anime) {
	client := http.Client{}
	req, _ := http.NewRequest("GET", "https://www.googleapis.com/customsearch/v1", nil)
	q := req.URL.Query()
	q.Add("q", a.Name)
	q.Add("searchType", "image")
	q.Add("imgSize", "large")
	q.Add("key", os.Getenv("CH_COMPILE_NYAA_GOOGLE_KEY"))
	q.Add("cx", os.Getenv("CH_COMPILE_NYAA_GOOGLE_CX"))

	req.URL.RawQuery = q.Encode()

	res, _ := client.Do(req)

	resBody, _ := ioutil.ReadAll(res.Body)
	var searchResult GoogleSearchResult
	json.Unmarshal(resBody, &searchResult)
	fmt.Printf("%+v\n", searchResult)

	a.Thumbnail = searchResult.Items[0].Link
	fmt.Printf("THUMBUP: %+v\n", a)
}

func (a Anime) AfterUpdate(db *gorm.DB) {
	fmt.Println("Afterupdate!")
	if a.AutoDownloadResolution != 0 {
		var torrents []Torrent
		db.
			Joins("INNER JOIN episodes ON torrents.episode_id = episodes.ID").
			Joins("INNER JOIN animes ON episodes.anime_id = animes.ID").
			Where("animes.ID = ? AND torrents.Resolution = ? AND torrents.Downloaded = ?", a.ID, a.AutoDownloadResolution, false).
			Find(&torrents)

		for _, t := range torrents {
			fmt.Printf("Downloading %+v", t)
			download(t)
		}
	}
}

// DelugeMethod represents a generic Deluge JSON Method
type DelugeMethod struct {
	ID     uint     `json:"id"`
	Method string   `json:"method"`
	Params []string `json:"params"`
}

// DelugeWebAddTorrentMethod represents a generic Deluge JSON Method
type DelugeWebAddTorrentMethod struct {
	ID     uint              `json:"id"`
	Method string            `json:"method"`
	Params [][]DelugeTorrent `json:"params"`
}

// DelugeTorrent represents a torrent
type DelugeTorrent struct {
	Path    string               `json:"path"`
	Options DelugeTorrentOptions `json:"options"`
}

// DelugeTorrentOptions reprents
type DelugeTorrentOptions struct {
	FilePriorities            []string `json:"file_priorities"`
	CompactAllocation         bool     `json:"compact_allocation"`
	DownloadLocation          string   `json:"download_location"`
	MoveCompleted             bool     `json:"move_completed"`
	MoveCompletedPath         string   `json:"move_completed_path"`
	MaxConnections            int      `json:"max_connections"`
	MaxDownloadSpeed          int      `json:"max_download_speed"`
	MaxUploadSlots            int      `json:"max_upload_slots"`
	MaxUploadSpeed            int      `json:"max_upload_speed"`
	PrioritizeFirstLastPieces bool     `json:"prioritize_first_last_pieces"`
}

func download(t Torrent) {
	jar, _ := cookiejar.New(nil)
	client := &http.Client{
		Jar: jar,
	}

	login, _ := json.Marshal(&DelugeMethod{
		Method: "auth.login",
		Params: []string{""},
		ID:     1,
	})

	fmt.Printf("data: %+v\n", string(login))

	res, err := client.Post(os.Getenv("CH_COMPILE_NYAA_DELUGE_URL")+"/json", "application/json", bytes.NewReader(login))

	fmt.Println("err:", err)
	body, _ := ioutil.ReadAll(res.Body)
	fmt.Printf("status: %d\nbody:%+v\n", res.StatusCode, string(body))

	torrentJson := DelugeTorrent{
		Path: t.Link,
		Options: DelugeTorrentOptions{
			CompactAllocation:         false,
			DownloadLocation:          "/downloads",
			FilePriorities:            []string{},
			MaxConnections:            -1,
			MaxDownloadSpeed:          -1,
			MaxUploadSlots:            -1,
			MaxUploadSpeed:            -1,
			MoveCompleted:             false,
			MoveCompletedPath:         "/downloads",
			PrioritizeFirstLastPieces: false,
		},
	}

	webAddTorrents, _ := json.Marshal(&DelugeWebAddTorrentMethod{
		ID:     2,
		Method: "web.add_torrents",
		Params: [][]DelugeTorrent{[]DelugeTorrent{torrentJson}},
	})

	fmt.Printf("Sending to DELUGE: %+v", string(webAddTorrents))

	res, err = client.Post(os.Getenv("CH_COMPILE_NYAA_DELUGE_URL")+"/json", "application/json", bytes.NewReader(webAddTorrents))
	fmt.Println("err:", err)
	body, _ = ioutil.ReadAll(res.Body)
	fmt.Printf("status: %d\nbody:%+v\n", res.StatusCode, string(body))
}

func initTicker() {
	ticker := time.NewTicker(time.Second * 10)
	go func() {
		titleRe := regexp.MustCompile(`^\[(.+?)\]\s+([^\[\]]+?)\s*-\s+(\d+)\s+.*(720|1080|480).*\.(mp4|mkv)$`)

		for range ticker.C {
			fp := gofeed.NewParser()
			feed, _ := fp.ParseURL("https://nyaa.si/?page=rss&m=true&c=1_2&f=0&q=720p")

			fmt.Println(feed.Title)

			for _, element := range feed.Items {
				matches := titleRe.FindStringSubmatch(element.Title)
				fmt.Println(matches)
				if len(matches) == 0 {
					continue
				}
				// 0: full
				// 1: group
				// 2: Anime
				// 3: Episode Number
				// 4: Resolution
				// 5: Extension
				var anime Anime
				fmt.Println(matches[2])
				//db.Where(&Anime{Name: matches[2]}).FirstOrCreate(&anime)
				db.FirstOrInit(&anime, &Anime{Name: matches[2]})

				fmt.Println(anime)
				if db.NewRecord(anime) {
					//UpdateThumbnail(&anime)
				}

				subbingGroup := SubbingGroup{}
				db.FirstOrCreate(&subbingGroup, &SubbingGroup{Name: matches[1]})

				anime.SubbingGroups = append(anime.SubbingGroups, subbingGroup)

				db.Save(&anime)

				episode := Episode{}
				episodeNr, _ := strconv.Atoi(matches[3])
				db.FirstOrCreate(&episode, &Episode{Number: uint(episodeNr), AnimeID: anime.ID})

				resolution, _ := strconv.Atoi(matches[4])

				torrent := Torrent{}
				db.FirstOrInit(&torrent, &Torrent{
					Title: element.Title,
				})

				torrent.EpisodeID = episode.ID
				torrent.Extension = matches[5]
				torrent.Link = element.Link
				torrent.PubDate = *element.PublishedParsed
				torrent.Resolution = uint(resolution)
				torrent.Title = element.Title
				torrent.SubbingGroupID = subbingGroup.ID

				db.Save(&torrent)

			}

		}
	}()
}
<commit_msg>panic<commit_after>package main

import (
	"bytes"
	"net/http"
	"net/http/cookiejar"
	"strconv"

	"fmt"
	"time"

	"regexp"

	"encoding/json"

	"io/ioutil"

	"os"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/mmcdole/gofeed"
)

type Anime struct {
	ID                     uint           `json:"id"`
	Name                   string         `gorm:"index" json:"name"`
	Thumbnail              string         `json:"thumbnail"`
	AutoDownloadResolution uint           `json:"auto_download_resolution"`
	AutoDownloadGroupID    uint           `json:"auto_download_group_id"`
	SubbingGroups          []SubbingGroup `json:"subbing_groups" gorm:"many2many:anime_subbing_groups;"`
	Episodes               []Episode      `json:"episodes"`
}

type Episode struct {
	ID       uint      `json:"id"`
	AnimeID  uint      `json:"anime_id"`
	Number   uint      `gorm:"index"`
	Torrents []Torrent `json:"torrents"`
}

type Torrent struct {
	ID             uint      `json:"id"`
	EpisodeID      uint      `json:"episode_id"`
	SubbingGroupID uint      `json:"subbing_group_id"`
	Link           string    `json:"link"`
	Extension      string    `json:"extension"`
	PubDate        time.Time `gorm:"index" json:"pub_date"`
	Resolution     uint      `json:"resolution"`
	Title          string    `json:"title"`
	Downloaded     bool      `gorm:"index" json:"downloaded"`
}

type SubbingGroup struct {
	ID   uint   `json:"id"`
	Name string `gorm:"index" json:"name"`
}

type GoogleSearchResult struct {
	Kind string `json:"kind"`
	URL  struct {
		Type     string `json:"type"`
		Template string `json:"template"`
	} `json:"url"`
	Queries struct {
		Request []struct {
			Title          string `json:"title"`
			TotalResults   string `json:"totalResults"`
			SearchTerms    string `json:"searchTerms"`
			Count          int    `json:"count"`
			StartIndex     int    `json:"startIndex"`
			InputEncoding  string `json:"inputEncoding"`
			OutputEncoding string `json:"outputEncoding"`
			Safe           string `json:"safe"`
			Cx             string `json:"cx"`
			SearchType     string `json:"searchType"`
			ImgSize        string `json:"imgSize"`
		} `json:"request"`
		NextPage []struct {
			Title          string `json:"title"`
			TotalResults   string `json:"totalResults"`
			SearchTerms    string `json:"searchTerms"`
			Count          int    `json:"count"`
			StartIndex     int    `json:"startIndex"`
			InputEncoding  string `json:"inputEncoding"`
			OutputEncoding string `json:"outputEncoding"`
			Safe           string `json:"safe"`
			Cx             string `json:"cx"`
			SearchType     string `json:"searchType"`
			ImgSize        string `json:"imgSize"`
		} `json:"nextPage"`
	} `json:"queries"`
	Context struct {
		Title string `json:"title"`
	} `json:"context"`
	SearchInformation struct {
		SearchTime            float64 `json:"searchTime"`
		FormattedSearchTime   string  `json:"formattedSearchTime"`
		TotalResults          string  `json:"totalResults"`
		FormattedTotalResults string  `json:"formattedTotalResults"`
	} `json:"searchInformation"`
	Items []struct {
		Kind        string `json:"kind"`
		Title       string `json:"title"`
		HTMLTitle   string `json:"htmlTitle"`
		Link        string `json:"link"`
		DisplayLink string `json:"displayLink"`
		Snippet     string `json:"snippet"`
		HTMLSnippet string `json:"htmlSnippet"`
		Mime        string `json:"mime"`
		Image       struct {
			ContextLink     string `json:"contextLink"`
			Height          int    `json:"height"`
			Width           int    `json:"width"`
			ByteSize        int    `json:"byteSize"`
			ThumbnailLink   string `json:"thumbnailLink"`
			ThumbnailHeight int    `json:"thumbnailHeight"`
			ThumbnailWidth  int    `json:"thumbnailWidth"`
		} `json:"image"`
	} `json:"items"`
}

var db *gorm.DB

type AutoDownloadRequest struct {
	Resolution     uint `json:"resolution"`
	SubbingGroupID uint `json:"subbing_group_id"`
}

func main() {
	var err error
	db, err = gorm.Open("sqlite3", "data/nyaatorrentler.db")
	if err != nil {
		panic("Failed to connect to database...")
	}
	// Migrate the schema
	db.AutoMigrate(&Anime{}, &Episode{}, &Torrent{}, &SubbingGroup{})

	initTicker()

	e := echo.New()
	e.Use(middleware.Logger())
	e.Static("/", "public")
	e.GET("/api/animes", func(c echo.Context) error {
		var anime []Anime
		//db.Joins("LEFT JOIN episodes ON episodes.anime_id = animes.ID").Joins("LEFT JOIN torrents ON torrents.episode_id = episodes.ID").Limit(100).Order("ID desc").Find(&anime)
		db.Preload("Episodes").Preload("SubbingGroups").Preload("Episodes.Torrents").Find(&anime)
		return c.JSON(http.StatusOK, anime)
	})

	e.POST("/api/animes/:id/toggle", func(c echo.Context) error {
		id, _ := strconv.Atoi(c.Param("id"))
		anime := Anime{ID: uint(id)}
		var autoDownloadRequest AutoDownloadRequest
		c.Bind(&autoDownloadRequest)
		db.First(&anime)
		if anime.AutoDownloadResolution == 0 {
			anime.AutoDownloadResolution = autoDownloadRequest.Resolution
			anime.AutoDownloadGroupID = autoDownloadRequest.SubbingGroupID
		} else {
			anime.AutoDownloadResolution = 0
			anime.AutoDownloadGroupID = 0
		}
		db.Save(anime)
		return c.JSON(http.StatusOK, &anime)
	})

	e.POST("/api/torrent/:id/download", func(c echo.Context) error {
		var torrent Torrent
		db.First(&torrent, c.Param("id"))
		download(torrent)
		return c.JSON(http.StatusOK, torrent)
	})

	e.Logger.Fatal(e.Start(":1323"))
}

func UpdateThumbnail(a *Anime) {
	client := http.Client{}
	req, _ := http.NewRequest("GET", "https://www.googleapis.com/customsearch/v1", nil)
	q := req.URL.Query()
	q.Add("q", a.Name)
	q.Add("searchType", "image")
	q.Add("imgSize", "large")
	q.Add("key", os.Getenv("CH_COMPILE_NYAA_GOOGLE_KEY"))
	q.Add("cx", os.Getenv("CH_COMPILE_NYAA_GOOGLE_CX"))

	req.URL.RawQuery = q.Encode()

	res, _ := client.Do(req)

	resBody, _ := ioutil.ReadAll(res.Body)
	var searchResult GoogleSearchResult
	json.Unmarshal(resBody, &searchResult)
	fmt.Printf("%+v\n", searchResult)

	a.Thumbnail = searchResult.Items[0].Link
	fmt.Printf("THUMBUP: %+v\n", a)
}

func (a Anime) AfterUpdate(db *gorm.DB) {
	fmt.Println("Afterupdate!")
	if a.AutoDownloadResolution != 0 {
		var torrents []Torrent
		db.
			Joins("INNER JOIN episodes ON torrents.episode_id = episodes.ID").
			Joins("INNER JOIN animes ON episodes.anime_id = animes.ID").
			Where("animes.ID = ? AND torrents.Resolution = ? AND torrents.Downloaded = ?", a.ID, a.AutoDownloadResolution, false).
			Find(&torrents)

		for _, t := range torrents {
			fmt.Printf("Downloading %+v", t)
			download(t)
		}
	}
}

// DelugeMethod represents a generic Deluge JSON Method
type DelugeMethod struct {
	ID     uint     `json:"id"`
	Method string   `json:"method"`
	Params []string `json:"params"`
}

// DelugeWebAddTorrentMethod represents a generic Deluge JSON Method
type DelugeWebAddTorrentMethod struct {
	ID     uint              `json:"id"`
	Method string            `json:"method"`
	Params [][]DelugeTorrent `json:"params"`
}

// DelugeTorrent represents a torrent
type DelugeTorrent struct {
	Path    string               `json:"path"`
	Options DelugeTorrentOptions `json:"options"`
}

// DelugeTorrentOptions reprents
type DelugeTorrentOptions struct {
	FilePriorities            []string `json:"file_priorities"`
	CompactAllocation         bool     `json:"compact_allocation"`
	DownloadLocation          string   `json:"download_location"`
	MoveCompleted             bool     `json:"move_completed"`
	MoveCompletedPath         string   `json:"move_completed_path"`
	MaxConnections            int      `json:"max_connections"`
	MaxDownloadSpeed          int      `json:"max_download_speed"`
	MaxUploadSlots            int      `json:"max_upload_slots"`
	MaxUploadSpeed            int      `json:"max_upload_speed"`
	PrioritizeFirstLastPieces bool     `json:"prioritize_first_last_pieces"`
}

func download(t Torrent) {
	jar, _ := cookiejar.New(nil)
	client := &http.Client{
		Jar: jar,
	}

	login, _ := json.Marshal(&DelugeMethod{
		Method: "auth.login",
		Params: []string{""},
		ID:     1,
	})

	fmt.Printf("data: %+v\n", string(login))

	res, err := client.Post(os.Getenv("CH_COMPILE_NYAA_DELUGE_URL")+"/json", "application/json", bytes.NewReader(login))

	fmt.Println("err:", err)
	body, _ := ioutil.ReadAll(res.Body)
	fmt.Printf("status: %d\nbody:%+v\n", res.StatusCode, string(body))

	torrentJson := DelugeTorrent{
		Path: t.Link,
		Options: DelugeTorrentOptions{
			CompactAllocation:         false,
			DownloadLocation:          "/downloads",
			FilePriorities:            []string{},
			MaxConnections:            -1,
			MaxDownloadSpeed:          -1,
			MaxUploadSlots:            -1,
			MaxUploadSpeed:            -1,
			MoveCompleted:             false,
			MoveCompletedPath:         "/downloads",
			PrioritizeFirstLastPieces: false,
		},
	}

	webAddTorrents, _ := json.Marshal(&DelugeWebAddTorrentMethod{
		ID:     2,
		Method: "web.add_torrents",
		Params: [][]DelugeTorrent{[]DelugeTorrent{torrentJson}},
	})

	fmt.Printf("Sending to DELUGE: %+v", string(webAddTorrents))

	res, err = client.Post(os.Getenv("CH_COMPILE_NYAA_DELUGE_URL")+"/json", "application/json", bytes.NewReader(webAddTorrents))
	fmt.Println("err:", err)
	body, _ = ioutil.ReadAll(res.Body)
	fmt.Printf("status: %d\nbody:%+v\n", res.StatusCode, string(body))
}

func initTicker() {
	ticker := time.NewTicker(time.Second * 10)
	go func() {
		titleRe := regexp.MustCompile(`^\[(.+?)\]\s+([^\[\]]+?)\s*-\s+(\d+)\s+.*(720|1080|480).*\.(mp4|mkv)$`)

		for range ticker.C {
			fp := gofeed.NewParser()
			feed, err := fp.ParseURL("https://nyaa.si/?page=rss&m=true&c=1_2&f=0&q=720p")

			if err != nil {
				panic(err)
			}

			fmt.Println(feed.Title)

			for _, element := range feed.Items {
				matches := titleRe.FindStringSubmatch(element.Title)
				fmt.Println(matches)
				if len(matches) == 0 {
					continue
				}
				// 0: full
				// 1: group
				// 2: Anime
				// 3: Episode Number
				// 4: Resolution
				// 5: Extension
				var anime Anime
				fmt.Println(matches[2])
				//db.Where(&Anime{Name: matches[2]}).FirstOrCreate(&anime)
				db.FirstOrInit(&anime, &Anime{Name: matches[2]})

				fmt.Println(anime)
				if db.NewRecord(anime) {
					//UpdateThumbnail(&anime)
				}

				subbingGroup := SubbingGroup{}
				db.FirstOrCreate(&subbingGroup, &SubbingGroup{Name: matches[1]})

				anime.SubbingGroups = append(anime.SubbingGroups, subbingGroup)

				db.Save(&anime)

				episode := Episode{}
				episodeNr, _ := strconv.Atoi(matches[3])
				db.FirstOrCreate(&episode, &Episode{Number: uint(episodeNr), AnimeID: anime.ID})

				resolution, _ := strconv.Atoi(matches[4])

				torrent := Torrent{}
				db.FirstOrInit(&torrent, &Torrent{
					Title: element.Title,
				})

				torrent.EpisodeID = episode.ID
				torrent.Extension = matches[5]
				torrent.Link = element.Link
				torrent.PubDate = *element.PublishedParsed
				torrent.Resolution = uint(resolution)
				torrent.Title = element.Title
				torrent.SubbingGroupID = subbingGroup.ID

				db.Save(&torrent)

			}

		}
	}()
}
<|endoftext|>


<commit_before>package server

import (
	"io"
	"log"
	"net"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	"github.com/vito/garden/backend"
	"github.com/vito/garden/messagereader"
	protocol "github.com/vito/garden/protocol"
)

type WardenServer struct {
	socketPath string
	backend    backend.Backend
}

func New(socketPath string, backend backend.Backend) *WardenServer {
	return &WardenServer{
		socketPath: socketPath,
		backend:    backend,
	}
}

func (s *WardenServer) Start() error {
	listener, err := net.Listen("unix", s.socketPath)
	if err != nil {
		return err
	}

	go s.handleConnections(listener)

	return nil
}

func (s *WardenServer) handleConnections(listener net.Listener) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println("error accepting connection:", err)
			continue
		}

		go s.serveConnection(conn)
	}
}

func (s *WardenServer) serveConnection(conn net.Conn) {
	for {
		request, err := messagereader.ReadRequest(conn)
		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error reading request:", err)
			continue
		}

		var response proto.Message

		switch request.(type) {
		case *protocol.PingRequest:
			response = &protocol.PingResponse{}
		case *protocol.EchoRequest:
			response = &protocol.EchoResponse{
				Message: request.(*protocol.EchoRequest).Message,
			}
		case *protocol.CreateRequest:
			createRequest := request.(*protocol.CreateRequest)

			bindMounts := []backend.BindMount{}

			for _, bm := range createRequest.GetBindMounts() {
				bindMount := backend.BindMount{
					SrcPath: bm.GetSrcPath(),
					DstPath: bm.GetDstPath(),
					Mode:    backend.BindMountMode(bm.GetMode()),
				}

				bindMounts = append(bindMounts, bindMount)
			}

			container, err := s.backend.Create(backend.ContainerSpec{
				Handle:     createRequest.GetHandle(),
				GraceTime:  time.Duration(createRequest.GetGraceTime()) * time.Second,
				RootFSPath: createRequest.GetRootfs(),
				Network:    createRequest.GetNetwork(),
				BindMounts: bindMounts,
			})

			if err == nil {
				response = &protocol.CreateResponse{
					Handle: proto.String(container.Handle()),
				}
			} else {
				response = &protocol.ErrorResponse{
					Message: proto.String(err.Error()),
				}
			}
		case *protocol.DestroyRequest:
			handle := request.(*protocol.DestroyRequest).GetHandle()

			err := s.backend.Destroy(handle)

			if err == nil {
				response = &protocol.DestroyResponse{}
			} else {
				response = &protocol.ErrorResponse{
					Message: proto.String(err.Error()),
				}
			}
		case *protocol.ListRequest:
			containers, err := s.backend.Containers()

			if err == nil {
				handles := []string{}

				for _, container := range containers {
					handles = append(handles, container.Handle())
				}

				response = &protocol.ListResponse{
					Handles: handles,
				}
			} else {
				response = &protocol.ErrorResponse{
					Message: proto.String(err.Error()),
				}
			}
		}

		if response == nil {
			log.Printf("unhandled request type: %T", request)
			continue
		}

		protocol.Messages(response).WriteTo(conn)
	}
}
<commit_msg>respond to unhandled requests with an error<commit_after>package server

import (
	"fmt"
	"io"
	"log"
	"net"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	"github.com/vito/garden/backend"
	"github.com/vito/garden/messagereader"
	protocol "github.com/vito/garden/protocol"
)

type WardenServer struct {
	socketPath string
	backend    backend.Backend
}

func New(socketPath string, backend backend.Backend) *WardenServer {
	return &WardenServer{
		socketPath: socketPath,
		backend:    backend,
	}
}

func (s *WardenServer) Start() error {
	listener, err := net.Listen("unix", s.socketPath)
	if err != nil {
		return err
	}

	go s.handleConnections(listener)

	return nil
}

func (s *WardenServer) handleConnections(listener net.Listener) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println("error accepting connection:", err)
			continue
		}

		go s.serveConnection(conn)
	}
}

func (s *WardenServer) serveConnection(conn net.Conn) {
	for {
		request, err := messagereader.ReadRequest(conn)
		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error reading request:", err)
			continue
		}

		var response proto.Message

		switch request.(type) {
		case *protocol.PingRequest:
			response = &protocol.PingResponse{}
		case *protocol.EchoRequest:
			response = &protocol.EchoResponse{
				Message: request.(*protocol.EchoRequest).Message,
			}
		case *protocol.CreateRequest:
			createRequest := request.(*protocol.CreateRequest)

			bindMounts := []backend.BindMount{}

			for _, bm := range createRequest.GetBindMounts() {
				bindMount := backend.BindMount{
					SrcPath: bm.GetSrcPath(),
					DstPath: bm.GetDstPath(),
					Mode:    backend.BindMountMode(bm.GetMode()),
				}

				bindMounts = append(bindMounts, bindMount)
			}

			container, err := s.backend.Create(backend.ContainerSpec{
				Handle:     createRequest.GetHandle(),
				GraceTime:  time.Duration(createRequest.GetGraceTime()) * time.Second,
				RootFSPath: createRequest.GetRootfs(),
				Network:    createRequest.GetNetwork(),
				BindMounts: bindMounts,
			})

			if err == nil {
				response = &protocol.CreateResponse{
					Handle: proto.String(container.Handle()),
				}
			} else {
				response = &protocol.ErrorResponse{
					Message: proto.String(err.Error()),
				}
			}
		case *protocol.DestroyRequest:
			handle := request.(*protocol.DestroyRequest).GetHandle()

			err := s.backend.Destroy(handle)

			if err == nil {
				response = &protocol.DestroyResponse{}
			} else {
				response = &protocol.ErrorResponse{
					Message: proto.String(err.Error()),
				}
			}
		case *protocol.ListRequest:
			containers, err := s.backend.Containers()

			if err == nil {
				handles := []string{}

				for _, container := range containers {
					handles = append(handles, container.Handle())
				}

				response = &protocol.ListResponse{
					Handles: handles,
				}
			} else {
				response = &protocol.ErrorResponse{
					Message: proto.String(err.Error()),
				}
			}
		}

		if response == nil {
			response = &protocol.ErrorResponse{
				Message: proto.String(
					fmt.Sprintf("unhandled request type: %T", request),
				),
			}
		}

		protocol.Messages(response).WriteTo(conn)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"net"
	"net/http"

	"github.com/ReformedDevs/anonbot/db"
	"github.com/ReformedDevs/anonbot/tweeter"
	"github.com/flosch/pongo2"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/sirupsen/logrus"

	_ "github.com/flosch/pongo2-addons"
)

// Server provides the web UI for interacting with the application. Users can
// login, post suggestions, and queue items if they have the appropriate
// permissions.
type Server struct {
	listener    net.Listener
	database    *db.Connection
	tweeter     *tweeter.Tweeter
	router      *mux.Router
	store       *sessions.CookieStore
	templateSet *pongo2.TemplateSet
	log         *logrus.Entry
	stoppedCh   chan bool
}

// New creates a new server with the specified configuration.
func New(cfg *Config) (*Server, error) {
	l, err := net.Listen("tcp", cfg.Addr)
	if err != nil {
		return nil, err
	}
	var (
		s = &Server{
			listener:    l,
			database:    cfg.Database,
			tweeter:     cfg.Tweeter,
			router:      mux.NewRouter(),
			store:       sessions.NewCookieStore([]byte(cfg.SecretKey)),
			templateSet: pongo2.NewSet("", &b0xLoader{}),
			log:         logrus.WithField("context", "server"),
			stoppedCh:   make(chan bool),
		}
		server = http.Server{
			Handler: s,
		}
	)
	s.router.HandleFunc("/", s.index)
	s.router.HandleFunc("/login", s.login)
	s.router.HandleFunc("/register", s.register)
	s.router.HandleFunc("/logout", s.requireLogin(s.logout))
	s.router.HandleFunc("/suggestions", s.requireLogin(s.suggestions))
	s.router.HandleFunc("/suggestions/new", s.requireLogin(s.editSuggestion))
	s.router.HandleFunc("/suggestions/{id:[0-9]+}/edit", s.requireLogin(s.editSuggestion))
	s.router.HandleFunc("/suggestions/{id:[0-9]+}/queue", s.requireAdmin(s.queueSuggestion))
	s.router.HandleFunc("/suggestions/{id:[0-9]+}/delete", s.requireLogin(s.deleteSuggestion))
	s.router.HandleFunc("/queue", s.requireLogin(s.queue))
	s.router.HandleFunc("/queue/{id:[0-9]+}/delete", s.requireAdmin(s.deleteQueueItem))
	s.router.HandleFunc("/accounts", s.requireAdmin(s.accounts))
	s.router.HandleFunc("/accounts/new", s.requireAdmin(s.editAccount))
	s.router.HandleFunc("/accounts/{id:[0-9]+}/edit", s.requireAdmin(s.editAccount))
	s.router.HandleFunc("/accounts/{id:[0-9]+}/delete", s.requireAdmin(s.deleteAccount))
	s.router.HandleFunc("/users", s.requireAdmin(s.users))
	s.router.HandleFunc("/users/new", s.requireAdmin(s.editUser))
	s.router.HandleFunc("/users/{id:[0-9]+}/edit", s.requireAdmin(s.editUser))
	s.router.HandleFunc("/users/{id:[0-9]+}/delete", s.requireAdmin(s.deleteUser))
	s.router.PathPrefix("/static").Handler(http.FileServer(HTTP))
	go func() {
		defer close(s.stoppedCh)
		defer s.log.Info("web server has stopped")
		s.log.Info("starting web server...")
		if err := server.Serve(l); err != nil {
			s.log.Error(err.Error())
		}
	}()
	return s, nil
}

// ServeHTTP does preparatory work for the handlers. It attempts to load the
// user from the database if authenticated and ensures that POST requests have
// their forms parsed.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodPost {
		if err := r.ParseForm(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	r = s.loadUser(r)
	s.router.ServeHTTP(w, r)
}

// Close shuts down the server and waits for it to complete.
func (s *Server) Close() {
	s.listener.Close()
	<-s.stoppedCh
}
<commit_msg>Add second level router to avoid loading user for static requests.<commit_after>package server

import (
	"net"
	"net/http"

	"github.com/ReformedDevs/anonbot/db"
	"github.com/ReformedDevs/anonbot/tweeter"
	"github.com/flosch/pongo2"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/sirupsen/logrus"

	_ "github.com/flosch/pongo2-addons"
)

// Server provides the web UI for interacting with the application. Users can
// login, post suggestions, and queue items if they have the appropriate
// permissions.
type Server struct {
	listener    net.Listener
	database    *db.Connection
	tweeter     *tweeter.Tweeter
	router      *mux.Router
	store       *sessions.CookieStore
	templateSet *pongo2.TemplateSet
	log         *logrus.Entry
	stoppedCh   chan bool
}

// New creates a new server with the specified configuration.
func New(cfg *Config) (*Server, error) {
	l, err := net.Listen("tcp", cfg.Addr)
	if err != nil {
		return nil, err
	}
	var (
		router = mux.NewRouter()
		s      = &Server{
			listener:    l,
			database:    cfg.Database,
			tweeter:     cfg.Tweeter,
			router:      mux.NewRouter(),
			store:       sessions.NewCookieStore([]byte(cfg.SecretKey)),
			templateSet: pongo2.NewSet("", &b0xLoader{}),
			log:         logrus.WithField("context", "server"),
			stoppedCh:   make(chan bool),
		}
		server = http.Server{
			Handler: router,
		}
	)
	s.router.HandleFunc("/", s.index)
	s.router.HandleFunc("/login", s.login)
	s.router.HandleFunc("/register", s.register)
	s.router.HandleFunc("/logout", s.requireLogin(s.logout))
	s.router.HandleFunc("/suggestions", s.requireLogin(s.suggestions))
	s.router.HandleFunc("/suggestions/new", s.requireLogin(s.editSuggestion))
	s.router.HandleFunc("/suggestions/{id:[0-9]+}/edit", s.requireLogin(s.editSuggestion))
	s.router.HandleFunc("/suggestions/{id:[0-9]+}/queue", s.requireAdmin(s.queueSuggestion))
	s.router.HandleFunc("/suggestions/{id:[0-9]+}/delete", s.requireLogin(s.deleteSuggestion))
	s.router.HandleFunc("/queue", s.requireLogin(s.queue))
	s.router.HandleFunc("/queue/{id:[0-9]+}/delete", s.requireAdmin(s.deleteQueueItem))
	s.router.HandleFunc("/accounts", s.requireAdmin(s.accounts))
	s.router.HandleFunc("/accounts/new", s.requireAdmin(s.editAccount))
	s.router.HandleFunc("/accounts/{id:[0-9]+}/edit", s.requireAdmin(s.editAccount))
	s.router.HandleFunc("/accounts/{id:[0-9]+}/delete", s.requireAdmin(s.deleteAccount))
	s.router.HandleFunc("/users", s.requireAdmin(s.users))
	s.router.HandleFunc("/users/new", s.requireAdmin(s.editUser))
	s.router.HandleFunc("/users/{id:[0-9]+}/edit", s.requireAdmin(s.editUser))
	s.router.HandleFunc("/users/{id:[0-9]+}/delete", s.requireAdmin(s.deleteUser))
	router.PathPrefix("/static").Handler(http.FileServer(HTTP))
	router.PathPrefix("/").Handler(s)
	go func() {
		defer close(s.stoppedCh)
		defer s.log.Info("web server has stopped")
		s.log.Info("starting web server...")
		if err := server.Serve(l); err != nil {
			s.log.Error(err.Error())
		}
	}()
	return s, nil
}

// ServeHTTP does preparatory work for the handlers. It attempts to load the
// user from the database if authenticated and ensures that POST requests have
// their forms parsed.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodPost {
		if err := r.ParseForm(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	r = s.loadUser(r)
	s.router.ServeHTTP(w, r)
}

// Close shuts down the server and waits for it to complete.
func (s *Server) Close() {
	s.listener.Close()
	<-s.stoppedCh
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"io/ioutil"
	"net"
	"net/http"
	"reflect"
	"strings"
	"time"

	"github.com/getlantern/errors"
	"github.com/getlantern/golog"
	"github.com/getlantern/ops"
	"github.com/getlantern/proxy"
	"github.com/getlantern/proxy/filters"
	"github.com/getlantern/tlsdefaults"

	"github.com/getlantern/http-proxy/buffers"
	"github.com/getlantern/http-proxy/listeners"
)

var (
	testingLocal = false
	log          = golog.LoggerFor("server")
)

type listenerGenerator func(net.Listener) net.Listener

// Opts are used to configure a Server
type Opts struct {
	IdleTimeout time.Duration
	Filter      filters.Filter
	Dial        proxy.DialFunc
}

// Server is an HTTP proxy server.
type Server struct {
	// Allow is a function that determines whether or not to allow connections
	// from the given IP address. If unspecified, all connections are allowed.
	Allow              func(string) bool
	proxy              proxy.Proxy
	listenerGenerators []listenerGenerator
}

// New constructs a new HTTP proxy server using the given options
func New(opts *Opts) *Server {
	p, _ := proxy.New(&proxy.Opts{
		IdleTimeout:        opts.IdleTimeout,
		Dial:               opts.Dial,
		Filter:             opts.Filter,
		BufferSource:       buffers.Pool(),
		OKWaitsForUpstream: true,
		OnError: func(ctx filters.Context, req *http.Request, read bool, err error) *http.Response {
			status := http.StatusBadGateway
			if read {
				status = http.StatusBadRequest
			}
			return &http.Response{
				Request:    req,
				StatusCode: status,
				Body:       ioutil.NopCloser(strings.NewReader(err.Error())),
			}
		},
	})
	return &Server{
		proxy: p,
	}
}

func (s *Server) AddListenerWrappers(listenerGens ...listenerGenerator) {
	for _, g := range listenerGens {
		s.listenerGenerators = append(s.listenerGenerators, g)
	}
}

func (s *Server) ListenAndServeHTTP(addr string, readyCb func(addr string)) error {
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	log.Debugf("Listen http on %s", addr)
	return s.serve(s.wrapListenerIfNecessary(listener), readyCb)
}

func (s *Server) ListenAndServeHTTPS(addr, keyfile, certfile string, readyCb func(addr string)) error {
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	listener, err := tlsdefaults.NewListener(s.wrapListenerIfNecessary(l), keyfile, certfile)
	if err != nil {
		return err
	}
	log.Debugf("Listen https on %s", addr)
	return s.serve(listener, readyCb)
}

func (s *Server) Serve(listener net.Listener, readyCb func(addr string)) error {
	return s.serve(s.wrapListenerIfNecessary(listener), readyCb)
}

func (s *Server) serve(listener net.Listener, readyCb func(addr string)) error {
	l := listeners.NewDefaultListener(listener)

	for _, wrap := range s.listenerGenerators {
		l = wrap(l)
	}

	if readyCb != nil {
		readyCb(l.Addr().String())
	}

	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		conn, err := l.Accept()
		if err != nil {
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				// delay code based on net/http.Server
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				log.Errorf("http: Accept error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return errors.New("Error accepting: %v", err)
		}
		tempDelay = 0
		s.handle(conn)
	}
}

func (s *Server) handle(conn net.Conn) {
	wrapConn, isWrapConn := conn.(listeners.WrapConn)
	if isWrapConn {
		wrapConn.OnState(http.StateNew)
	}
	go s.doHandle(conn, isWrapConn, wrapConn)
}

func (s *Server) doHandle(conn net.Conn, isWrapConn bool, wrapConn listeners.WrapConn) {
	log.Debug(conn)
	log.Debug(conn.RemoteAddr())
	clientIP := ""
	remoteAddr := conn.RemoteAddr()
	if remoteAddr != nil {
		clientIP, _, _ = net.SplitHostPort(remoteAddr.String())
	}
	op := ops.Begin("http_proxy_handle").Set("client_ip", clientIP)
	defer op.End()

	defer func() {
		p := recover()
		if p != nil {
			err := log.Errorf("Caught panic handling connection from %v: %v", conn.RemoteAddr(), p)
			if op != nil {
				op.FailIf(err)
			}
			safeClose(conn)
		}
	}()

	err := s.proxy.Handle(context.Background(), conn, conn)
	if err != nil {
		op.FailIf(log.Errorf("Error handling connection from %v: %v", conn.RemoteAddr(), err))
	}
	if isWrapConn {
		wrapConn.OnState(http.StateClosed)
	}
}

func safeClose(conn net.Conn) {
	defer func() {
		p := recover()
		if p != nil {
			log.Errorf("Panic on closing connection from %v: %v", conn.RemoteAddr(), p)
		}
	}()

	conn.Close()
}

func (s *Server) wrapListenerIfNecessary(l net.Listener) net.Listener {
	if s.Allow != nil {
		log.Debug("Wrapping listener with Allow")
		return &allowinglistener{l, s.Allow}
	}
	return l
}

type allowinglistener struct {
	wrapped net.Listener
	allow   func(string) bool
}

func (l *allowinglistener) Accept() (net.Conn, error) {
	conn, err := l.wrapped.Accept()
	if err != nil {
		return conn, err
	}

	ip := ""
	remoteAddr := conn.RemoteAddr()
	switch addr := remoteAddr.(type) {
	case *net.TCPAddr:
		ip = addr.IP.String()
	case *net.UDPAddr:
		ip = addr.IP.String()
	default:
		log.Errorf("Remote addr %v is of unknown type %v, unable to determine IP", remoteAddr, reflect.TypeOf(remoteAddr))
		return conn, err
	}
	if !l.allow(ip) {
		conn.Close()
		// Note - we don't return an error, because that causes http.Server to stop
		// serving.
	}

	return conn, err
}

func (l *allowinglistener) Close() error {
	return l.wrapped.Close()
}

func (l *allowinglistener) Addr() net.Addr {
	return l.wrapped.Addr()
}
<commit_msg>Removed unnecessary logging<commit_after>package server

import (
	"context"
	"io/ioutil"
	"net"
	"net/http"
	"reflect"
	"strings"
	"time"

	"github.com/getlantern/errors"
	"github.com/getlantern/golog"
	"github.com/getlantern/ops"
	"github.com/getlantern/proxy"
	"github.com/getlantern/proxy/filters"
	"github.com/getlantern/tlsdefaults"

	"github.com/getlantern/http-proxy/buffers"
	"github.com/getlantern/http-proxy/listeners"
)

var (
	testingLocal = false
	log          = golog.LoggerFor("server")
)

type listenerGenerator func(net.Listener) net.Listener

// Opts are used to configure a Server
type Opts struct {
	IdleTimeout time.Duration
	Filter      filters.Filter
	Dial        proxy.DialFunc
}

// Server is an HTTP proxy server.
type Server struct {
	// Allow is a function that determines whether or not to allow connections
	// from the given IP address. If unspecified, all connections are allowed.
	Allow              func(string) bool
	proxy              proxy.Proxy
	listenerGenerators []listenerGenerator
}

// New constructs a new HTTP proxy server using the given options
func New(opts *Opts) *Server {
	p, _ := proxy.New(&proxy.Opts{
		IdleTimeout:        opts.IdleTimeout,
		Dial:               opts.Dial,
		Filter:             opts.Filter,
		BufferSource:       buffers.Pool(),
		OKWaitsForUpstream: true,
		OnError: func(ctx filters.Context, req *http.Request, read bool, err error) *http.Response {
			status := http.StatusBadGateway
			if read {
				status = http.StatusBadRequest
			}
			return &http.Response{
				Request:    req,
				StatusCode: status,
				Body:       ioutil.NopCloser(strings.NewReader(err.Error())),
			}
		},
	})
	return &Server{
		proxy: p,
	}
}

func (s *Server) AddListenerWrappers(listenerGens ...listenerGenerator) {
	for _, g := range listenerGens {
		s.listenerGenerators = append(s.listenerGenerators, g)
	}
}

func (s *Server) ListenAndServeHTTP(addr string, readyCb func(addr string)) error {
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	log.Debugf("Listen http on %s", addr)
	return s.serve(s.wrapListenerIfNecessary(listener), readyCb)
}

func (s *Server) ListenAndServeHTTPS(addr, keyfile, certfile string, readyCb func(addr string)) error {
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	listener, err := tlsdefaults.NewListener(s.wrapListenerIfNecessary(l), keyfile, certfile)
	if err != nil {
		return err
	}
	log.Debugf("Listen https on %s", addr)
	return s.serve(listener, readyCb)
}

func (s *Server) Serve(listener net.Listener, readyCb func(addr string)) error {
	return s.serve(s.wrapListenerIfNecessary(listener), readyCb)
}

func (s *Server) serve(listener net.Listener, readyCb func(addr string)) error {
	l := listeners.NewDefaultListener(listener)

	for _, wrap := range s.listenerGenerators {
		l = wrap(l)
	}

	if readyCb != nil {
		readyCb(l.Addr().String())
	}

	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		conn, err := l.Accept()
		if err != nil {
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				// delay code based on net/http.Server
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				log.Errorf("http: Accept error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return errors.New("Error accepting: %v", err)
		}
		tempDelay = 0
		s.handle(conn)
	}
}

func (s *Server) handle(conn net.Conn) {
	wrapConn, isWrapConn := conn.(listeners.WrapConn)
	if isWrapConn {
		wrapConn.OnState(http.StateNew)
	}
	go s.doHandle(conn, isWrapConn, wrapConn)
}

func (s *Server) doHandle(conn net.Conn, isWrapConn bool, wrapConn listeners.WrapConn) {
	clientIP := ""
	remoteAddr := conn.RemoteAddr()
	if remoteAddr != nil {
		clientIP, _, _ = net.SplitHostPort(remoteAddr.String())
	}
	op := ops.Begin("http_proxy_handle").Set("client_ip", clientIP)
	defer op.End()

	defer func() {
		p := recover()
		if p != nil {
			err := log.Errorf("Caught panic handling connection from %v: %v", conn.RemoteAddr(), p)
			if op != nil {
				op.FailIf(err)
			}
			safeClose(conn)
		}
	}()

	err := s.proxy.Handle(context.Background(), conn, conn)
	if err != nil {
		op.FailIf(log.Errorf("Error handling connection from %v: %v", conn.RemoteAddr(), err))
	}
	if isWrapConn {
		wrapConn.OnState(http.StateClosed)
	}
}

func safeClose(conn net.Conn) {
	defer func() {
		p := recover()
		if p != nil {
			log.Errorf("Panic on closing connection from %v: %v", conn.RemoteAddr(), p)
		}
	}()

	conn.Close()
}

func (s *Server) wrapListenerIfNecessary(l net.Listener) net.Listener {
	if s.Allow != nil {
		log.Debug("Wrapping listener with Allow")
		return &allowinglistener{l, s.Allow}
	}
	return l
}

type allowinglistener struct {
	wrapped net.Listener
	allow   func(string) bool
}

func (l *allowinglistener) Accept() (net.Conn, error) {
	conn, err := l.wrapped.Accept()
	if err != nil {
		return conn, err
	}

	ip := ""
	remoteAddr := conn.RemoteAddr()
	switch addr := remoteAddr.(type) {
	case *net.TCPAddr:
		ip = addr.IP.String()
	case *net.UDPAddr:
		ip = addr.IP.String()
	default:
		log.Errorf("Remote addr %v is of unknown type %v, unable to determine IP", remoteAddr, reflect.TypeOf(remoteAddr))
		return conn, err
	}
	if !l.allow(ip) {
		conn.Close()
		// Note - we don't return an error, because that causes http.Server to stop
		// serving.
	}

	return conn, err
}

func (l *allowinglistener) Close() error {
	return l.wrapped.Close()
}

func (l *allowinglistener) Addr() net.Addr {
	return l.wrapped.Addr()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/julienschmidt/httprouter"
	"github.com/rs/cors"
	"gopkg.in/mgo.v2"

	"github.com/kshvmdn/what-class-is-this/server/controllers"
)

func getSession() *mgo.Session {
	s, err := mgo.Dial("mongodb://localhost")

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

	return s
}

func main() {
	router := httprouter.New()

	cc := controllers.NewCourseController(getSession())

	router.GET("/api/course/now", cc.GetNow)
	router.GET("/api/course/single/:id", cc.GetById)

	port := os.Getenv("PORT")

	if port == "" {
		port = "3001"
	}

	log.Println("Listening at http://localhost:%s", port)

	handler := cors.Default().Handler(router)
	if err := http.ListenAndServe(fmt.Sprintf("localhost:%s", port), handler); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Fix log print in main method<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/julienschmidt/httprouter"
	"github.com/rs/cors"
	"gopkg.in/mgo.v2"

	"github.com/kshvmdn/what-class-is-this/server/controllers"
)

func getSession() *mgo.Session {
	s, err := mgo.Dial("mongodb://localhost")

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

	return s
}

func main() {
	router := httprouter.New()

	cc := controllers.NewCourseController(getSession())

	router.GET("/api/course/now", cc.GetNow)
	router.GET("/api/course/single/:id", cc.GetById)

	port := os.Getenv("PORT")

	if port == "" {
		port = "3001"
	}

	log.Printf("Listening at http://localhost:%s\n", port)

	handler := cors.Default().Handler(router)
	if err := http.ListenAndServe(fmt.Sprintf("localhost:%s", port), handler); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package leap

import (
	"testing"
)

var testCases = []struct {
	year        int
	expected    bool
	description string
}{
	{1996, true, "a vanilla leap year"},
	{1997, false, "a normal year"},
	{1900, false, "a century"},
	{2400, true, "an exceptional century"},
}

func TestLeapYears(t *testing.T) {
	for _, test := range testCases {
		observed := IsLeap(test.year)
		if observed != test.expected {
			t.Fatalf("%v is %s", test.year, test.description)
		}
	}
}
<commit_msg>:arrow_forward: exercism.io update to base go Leap tests<commit_after>package leap

import (
	"testing"
)

var testCases = []struct {
	year        int
	expected    bool
	description string
}{
	{1996, true, "a vanilla leap year"},
	{1997, false, "a normal year"},
	{1900, false, "a century"},
	{2400, true, "an exceptional century"},
}

func TestLeapYears(t *testing.T) {
	for _, test := range testCases {
		observed := IsLeapYear(test.year)
		if observed != test.expected {
			t.Fatalf("%v is %s", test.year, test.description)
		}
	}
}
<|endoftext|>


<commit_before>// go_cover_reporter.go
package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"text/template"
)

type message struct {
	Name string
	Body string
}

const (
	filename = "persist.txt"
)

func main() {
	http.HandleFunc("/", handler) // each request calls handler function
	http.HandleFunc("/receiver", receiver)
	http.HandleFunc("/demo_badge", func(w http.ResponseWriter, r *http.Request) {
		coverBadge(w, readPercentageFromFile())
	})
	log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
	coverage := struct{ Percent float64 }{readPercentageFromFile()}
	err := pageTemplate.Execute(w, coverage)
	if err != nil {
		log.Print(err)
	}

}

func readPercentageFromFile() (i float64) {

	buffer, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalln(err)
	}

	percentString := string(buffer)
	percent, _ := strconv.ParseFloat(strings.TrimSpace(percentString), 64)

	return percent
}

var pageTemplate = template.Must(template.New("pageTemplate").Parse(`
<!DOCTYPE html>

Most recent reported code coverage is {{.Percent}}%

`))

func receiver(rw http.ResponseWriter, req *http.Request) {

	file, err := os.Create(filename)
	if err != nil {
		log.Fatalln(err)
	}

	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(string(body))

	var t message
	err = json.Unmarshal(body, &t)
	if err != nil {
		log.Fatalln(err)
	}

	re := regexp.MustCompile(`\d+.*\d*%`)

	numericalValue := re.FindString(string(t.Body))

	_, err = io.WriteString(file, numericalValue[:len(numericalValue)-1])
	if err != nil {
		log.Fatalln(err)
	}
	file.Close()

	log.Println(t.Body)
}

func dummyFunction(i int, j int) int {
	return i + j
}
<commit_msg>no-cache settings for gif badge/button<commit_after>// go_cover_reporter.go
package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"text/template"
	"time"
)

type message struct {
	Name string
	Body string
}

const (
	filename = "persist.txt"
)

func main() {
	http.HandleFunc("/", handler) // each request calls handler function
	http.HandleFunc("/receiver", receiver)
	http.HandleFunc("/demo_badge", func(w http.ResponseWriter, r *http.Request) {
		date := time.Now().Format(http.TimeFormat)
		log.Printf("%v", date)
		w.Header().Set("Content-Type", "image/gif")
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Date", date)
		w.Header().Set("Expires", date)
		coverBadge(w, readPercentageFromFile())
	})
	log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
	coverage := struct{ Percent float64 }{readPercentageFromFile()}
	err := pageTemplate.Execute(w, coverage)
	if err != nil {
		log.Print(err)
	}

}

func readPercentageFromFile() (i float64) {

	buffer, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalln(err)
	}

	percentString := string(buffer)
	percent, _ := strconv.ParseFloat(strings.TrimSpace(percentString), 64)

	return percent
}

var pageTemplate = template.Must(template.New("pageTemplate").Parse(`
<!DOCTYPE html>

Most recent reported code coverage is {{.Percent}}%

`))

func receiver(rw http.ResponseWriter, req *http.Request) {

	file, err := os.Create(filename)
	if err != nil {
		log.Fatalln(err)
	}

	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(string(body))

	var t message
	err = json.Unmarshal(body, &t)
	if err != nil {
		log.Fatalln(err)
	}

	re := regexp.MustCompile(`\d+.*\d*%`)

	numericalValue := re.FindString(string(t.Body))

	_, err = io.WriteString(file, numericalValue[:len(numericalValue)-1])
	if err != nil {
		log.Fatalln(err)
	}
	file.Close()

	log.Println(t.Body)
}

func dummyFunction(i int, j int) int {
	return i + j
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"os/exec"
	"strings"
	"time"

	"github.com/brotherlogic/goserver/utils"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	pbvt "github.com/brotherlogic/versiontracker/proto"
)

const (
	pendWait = time.Minute
)

var (
	ackQueueLen = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "gobuildslave_ackqueuelen",
		Help: "The size of the ack queue",
	})
)

func (s *Server) procAcks() {
	for job := range s.ackChan {
		ackQueueLen.Set(float64(len(s.ackChan)))
		ctx, cancel := utils.ManualContext("gobuildslaveack", time.Minute)
		conn, err := s.FDialSpecificServer(ctx, "versiontracker", s.Registry.GetIdentifier())
		if err != nil {
			s.DLog(ctx, fmt.Sprintf("Dial error: (%v), %v\n", job.GetJob(), err))
			s.ackChan <- job
		} else {

			client := pbvt.NewVersionTrackerServiceClient(conn)
			_, err = client.NewJob(ctx, &pbvt.NewJobRequest{Version: &pbb.Version{Job: job.GetJob()}})
			if err != nil {
				s.ackChan <- job
			}
			conn.Close()
		}
		cancel()

		// Don't rush the system
		time.Sleep(time.Second * 10)
	}
}

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	s.DLog(ctx, fmt.Sprintf("TRANS: %v\n", job))
	startState := job.State
	job.LastUpdateTime = time.Now().Unix()
	switch job.State {
	case pb.State_WARMUP:
		s.ackChan <- job
		job.State = pb.State_ACKNOWLEDGED
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		job.SubState = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		if !job.Job.Bootstrap {
			if key != "" {
				job.State = pb.State_BUILT
				job.RunningVersion = key
			} else {
				// Bootstrap this job since we don't have an initial version
				if job.Job.PartialBootstrap {
					job.Job.Bootstrap = true
				}
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		s.scheduler.wait(job.CommandKey)
		job.State = pb.State_BUILT
	case pb.State_BUILT:
		job.SubState = "Getting Output"
		output, _ := s.scheduler.getOutput(job.CommandKey)
		job.SubState = "Entering Lock"
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				job.SubState = "Sending Report"
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.State = pb.State_VERSION_CHECK
			// Don't check version on pb jobs
			if job.GetJob().GetPartialBootstrap() {
				s.doCopy(job)
				job.BuildFail = 0
				job.SubState = "Scheduling A Run post copy"
				key := s.scheduleRun(job)
				job.CommandKey = key
				job.StartTime = time.Now().Unix()
				job.State = pb.State_PENDING
				if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
					s.pendingMap[time.Now().Weekday()] = make(map[string]int)
				}
				s.pendingMap[time.Now().Weekday()][job.Job.Name]++
			}
		}
		job.SubState = "Out of case"
	case pb.State_VERSION_CHECK:
		s.doCopy(job)
		s.loadCurrentVersions()
		version, err := s.getLatestVersion(ctx, job.GetJob().Name, job.GetJob().GetGoPath())
		if err != nil {
			s.Log(fmt.Sprintf("Error getting version: %v", err))
			break
		}

		res, err := exec.Command("md5sum", fmt.Sprintf("/home/simon/gobuild/bin/%v", job.GetJob().GetName())).Output()
		if err != nil {
			s.Log(fmt.Sprintf("Error reading md5sum: %v", err))
		}
		elems := strings.Fields(string(res))
		job.RunningVersion = elems[0]

		s.versionsMutex.Lock()
		defer s.versionsMutex.Unlock()
		if elems[0] != version.GetVersion() {
			s.Log(fmt.Sprintf("Bad version on %v for %v -> %v vs %v", s.Registry.Identifier, job.GetJob().GetName(), elems[0], s.versions[job.GetJob().GetName()].Version))
			job.SubState = fmt.Sprintf("Dealing With Version Mismatch: %v", job.BuildFail)

			conn, err := s.FDialSpecificServer(ctx, "versiontracker", s.Registry.Identifier)
			if err != nil {
				s.Log(fmt.Sprintf("Unable to dial vt: %v", err))
			} else {
				defer conn.Close()
				vtc := pbvt.NewVersionTrackerServiceClient(conn)
				_, err = vtc.NewVersion(ctx, &pbvt.NewVersionRequest{
					Version: version,
				})
				s.Log(fmt.Sprintf("Requested new version: %v", err))
			}

			// Don't let the job sit here
			job.BuildFail++
			if job.BuildFail > 10 {
				job.State = pb.State_ACKNOWLEDGED

			}
			break
		}

		job.BuildFail = 0
		job.SubState = "Scheduling of the Run"
		key := s.scheduleRun(job)
		job.CommandKey = key
		job.StartTime = time.Now().Unix()
		job.State = pb.State_PENDING
		if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
			s.pendingMap[time.Now().Weekday()] = make(map[string]int)
		}
		s.pendingMap[time.Now().Weekday()][job.Job.Name]++

	case pb.State_PENDING:
		if job.Job.PartialBootstrap && job.Job.Bootstrap {
			job.Job.Bootstrap = false
		}
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			var err error
			if job.Job.Name == "discovery" {
				err = s.runOnChange()
			}
			code := status.Convert(err).Code()

			//Unavailable allows the job to die here
			if code == codes.OK || code == codes.Unavailable {
				// Validate that the job is alive
				if !s.isJobAlive(ctx, job) {
					s.Log(fmt.Sprintf("Job %v is not alive", job))
				}
				job.State = pb.State_RUNNING
			} else {
				s.Log(fmt.Sprintf("Cannot reregister: %v", err))
			}
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		output2, _ := s.scheduler.getErrOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 {
			s.scheduler.wait(job.CommandKey)
			s.stateMap[job.Job.Name] = fmt.Sprintf("ONLOCk = (%v, %v)", job, output)
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = (%v, %v)", job, output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, fmt.Sprintf("%v%v", output, output2))
			job.State = pb.State_DIED
		}

		if s.Registry != nil {
			entry, err := s.FFindSpecificServer(ctx, job.Job.Name, s.Registry.Identifier)
			if err != nil {
				if job.DiscoverCount > 30 {
					output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
					s.RaiseIssue("Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2))
				}
				job.DiscoverCount++
				s.Log(fmt.Sprintf("Missing discover for %+v, %v -> %v", job, entry, err))
			} else {
				job.Port = entry.GetPort()
				job.DiscoverCount = 0
			}
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	version, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	return version, nil
}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		// Block builds to prevent clashes
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name, block: true})
	}

	val, err := s.builder.build(ctx, job.Job)
	if err != nil {
		s.DLog(ctx, fmt.Sprintf("BUILD Error: %v\n", err))
		return ""
	}
	return val.Version
}

func (s *Server) doCopy(job *pb.JobAssignment) {
	//Copy over any existing new versions

	key := s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetJob().GetName()+".new", "$GOPATH/bin/"+job.GetJob().GetName()), base: job.GetJob().GetName()})
	s.scheduler.wait(key)

	key = s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetJob().GetName()+".nversion", "$GOPATH/bin/"+job.GetJob().GetName()+".version"), base: job.GetJob().GetName() + ".version"})
	s.scheduler.wait(key)
}

func (s *Server) scheduleRun(job *pb.JobAssignment) string {
	// Wait a while before starting the job JIC
	time.Sleep(time.Second * 2)

	c := s.translator.run(job.GetJob())
	return s.scheduler.Schedule(&rCommand{command: c, base: job.GetJob().GetName()})
}
<commit_msg>Set an initial version<commit_after>package main

import (
	"context"
	"fmt"
	"os/exec"
	"strings"
	"time"

	"github.com/brotherlogic/goserver/utils"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	pbvt "github.com/brotherlogic/versiontracker/proto"
)

const (
	pendWait = time.Minute
)

var (
	ackQueueLen = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "gobuildslave_ackqueuelen",
		Help: "The size of the ack queue",
	})
)

func (s *Server) procAcks() {
	for job := range s.ackChan {
		ackQueueLen.Set(float64(len(s.ackChan)))
		ctx, cancel := utils.ManualContext("gobuildslaveack", time.Minute)
		conn, err := s.FDialSpecificServer(ctx, "versiontracker", s.Registry.GetIdentifier())
		if err != nil {
			s.DLog(ctx, fmt.Sprintf("Dial error: (%v), %v\n", job.GetJob(), err))
			s.ackChan <- job
		} else {

			client := pbvt.NewVersionTrackerServiceClient(conn)
			_, err = client.NewJob(ctx, &pbvt.NewJobRequest{Version: &pbb.Version{Job: job.GetJob()}})
			if err != nil {
				s.ackChan <- job
			}
			conn.Close()
		}
		cancel()

		// Don't rush the system
		time.Sleep(time.Second * 10)
	}
}

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	s.DLog(ctx, fmt.Sprintf("TRANS: %v\n", job))
	startState := job.State
	job.LastUpdateTime = time.Now().Unix()
	switch job.State {
	case pb.State_WARMUP:
		res, err := exec.Command("md5sum", fmt.Sprintf("/home/simon/gobuild/bin/%v", job.GetJob().GetName())).Output()
		if err != nil {
			s.Log(fmt.Sprintf("Error reading md5sum: %v", err))
		}
		elems := strings.Fields(string(res))
		job.RunningVersion = elems[0]
		s.ackChan <- job
		job.State = pb.State_ACKNOWLEDGED
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		job.SubState = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		if !job.Job.Bootstrap {
			if key != "" {
				job.State = pb.State_BUILT
				job.RunningVersion = key
			} else {
				// Bootstrap this job since we don't have an initial version
				if job.Job.PartialBootstrap {
					job.Job.Bootstrap = true
				}
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		s.scheduler.wait(job.CommandKey)
		job.State = pb.State_BUILT
	case pb.State_BUILT:
		job.SubState = "Getting Output"
		output, _ := s.scheduler.getOutput(job.CommandKey)
		job.SubState = "Entering Lock"
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				job.SubState = "Sending Report"
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.State = pb.State_VERSION_CHECK
			// Don't check version on pb jobs
			if job.GetJob().GetPartialBootstrap() {
				s.doCopy(job)
				job.BuildFail = 0
				job.SubState = "Scheduling A Run post copy"
				key := s.scheduleRun(job)
				job.CommandKey = key
				job.StartTime = time.Now().Unix()
				job.State = pb.State_PENDING
				if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
					s.pendingMap[time.Now().Weekday()] = make(map[string]int)
				}
				s.pendingMap[time.Now().Weekday()][job.Job.Name]++
			}
		}
		job.SubState = "Out of case"
	case pb.State_VERSION_CHECK:
		s.doCopy(job)
		s.loadCurrentVersions()
		version, err := s.getLatestVersion(ctx, job.GetJob().Name, job.GetJob().GetGoPath())
		if err != nil {
			s.Log(fmt.Sprintf("Error getting version: %v", err))
			break
		}

		res, err := exec.Command("md5sum", fmt.Sprintf("/home/simon/gobuild/bin/%v", job.GetJob().GetName())).Output()
		if err != nil {
			s.Log(fmt.Sprintf("Error reading md5sum: %v", err))
		}
		elems := strings.Fields(string(res))
		job.RunningVersion = elems[0]

		s.versionsMutex.Lock()
		defer s.versionsMutex.Unlock()
		if elems[0] != version.GetVersion() {
			s.Log(fmt.Sprintf("Bad version on %v for %v -> %v vs %v", s.Registry.Identifier, job.GetJob().GetName(), elems[0], s.versions[job.GetJob().GetName()].Version))
			job.SubState = fmt.Sprintf("Dealing With Version Mismatch: %v", job.BuildFail)

			conn, err := s.FDialSpecificServer(ctx, "versiontracker", s.Registry.Identifier)
			if err != nil {
				s.Log(fmt.Sprintf("Unable to dial vt: %v", err))
			} else {
				defer conn.Close()
				vtc := pbvt.NewVersionTrackerServiceClient(conn)
				_, err = vtc.NewVersion(ctx, &pbvt.NewVersionRequest{
					Version: version,
				})
				s.Log(fmt.Sprintf("Requested new version: %v", err))
			}

			// Don't let the job sit here
			job.BuildFail++
			if job.BuildFail > 10 {
				job.State = pb.State_ACKNOWLEDGED

			}
			break
		}

		job.BuildFail = 0
		job.SubState = "Scheduling of the Run"
		key := s.scheduleRun(job)
		job.CommandKey = key
		job.StartTime = time.Now().Unix()
		job.State = pb.State_PENDING
		if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
			s.pendingMap[time.Now().Weekday()] = make(map[string]int)
		}
		s.pendingMap[time.Now().Weekday()][job.Job.Name]++

	case pb.State_PENDING:
		if job.Job.PartialBootstrap && job.Job.Bootstrap {
			job.Job.Bootstrap = false
		}
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			var err error
			if job.Job.Name == "discovery" {
				err = s.runOnChange()
			}
			code := status.Convert(err).Code()

			//Unavailable allows the job to die here
			if code == codes.OK || code == codes.Unavailable {
				// Validate that the job is alive
				if !s.isJobAlive(ctx, job) {
					s.Log(fmt.Sprintf("Job %v is not alive", job))
				}
				job.State = pb.State_RUNNING
			} else {
				s.Log(fmt.Sprintf("Cannot reregister: %v", err))
			}
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		output2, _ := s.scheduler.getErrOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 {
			s.scheduler.wait(job.CommandKey)
			s.stateMap[job.Job.Name] = fmt.Sprintf("ONLOCk = (%v, %v)", job, output)
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = (%v, %v)", job, output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, fmt.Sprintf("%v%v", output, output2))
			job.State = pb.State_DIED
		}

		if s.Registry != nil {
			entry, err := s.FFindSpecificServer(ctx, job.Job.Name, s.Registry.Identifier)
			if err != nil {
				if job.DiscoverCount > 30 {
					output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
					s.RaiseIssue("Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2))
				}
				job.DiscoverCount++
				s.Log(fmt.Sprintf("Missing discover for %+v, %v -> %v", job, entry, err))
			} else {
				job.Port = entry.GetPort()
				job.DiscoverCount = 0
			}
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	version, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	return version, nil
}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		// Block builds to prevent clashes
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name, block: true})
	}

	val, err := s.builder.build(ctx, job.Job)
	if err != nil {
		s.DLog(ctx, fmt.Sprintf("BUILD Error: %v\n", err))
		return ""
	}
	return val.Version
}

func (s *Server) doCopy(job *pb.JobAssignment) {
	//Copy over any existing new versions

	key := s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetJob().GetName()+".new", "$GOPATH/bin/"+job.GetJob().GetName()), base: job.GetJob().GetName()})
	s.scheduler.wait(key)

	key = s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetJob().GetName()+".nversion", "$GOPATH/bin/"+job.GetJob().GetName()+".version"), base: job.GetJob().GetName() + ".version"})
	s.scheduler.wait(key)
}

func (s *Server) scheduleRun(job *pb.JobAssignment) string {
	// Wait a while before starting the job JIC
	time.Sleep(time.Second * 2)

	c := s.translator.run(job.GetJob())
	return s.scheduler.Schedule(&rCommand{command: c, base: job.GetJob().GetName()})
}
<|endoftext|>


<commit_before>package main

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

// bindata_read reads the given file from disk. It returns an error on failure.
func bindata_read(path, name string) ([]byte, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		err = fmt.Errorf("Error reading asset %s at %s: %v", name, path, err)
	}
	return buf, err
}

// resources_templates_create_user_html reads file data from disk. It returns an error on failure.
func resources_templates_create_user_html() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/templates/create_user.html",
		"resources/templates/create_user.html",
	)
}

// resources_templates_index_html reads file data from disk. It returns an error on failure.
func resources_templates_index_html() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/templates/index.html",
		"resources/templates/index.html",
	)
}

// resources_templates_create_user_html_ reads file data from disk. It returns an error on failure.
func resources_templates_create_user_html_() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/templates/create_user.html~",
		"resources/templates/create_user.html~",
	)
}

// resources_templates_index_html_ reads file data from disk. It returns an error on failure.
func resources_templates_index_html_() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/templates/index.html~",
		"resources/templates/index.html~",
	)
}

// resources_css_site_css reads file data from disk. It returns an error on failure.
func resources_css_site_css() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/css/site.css",
		"resources/css/site.css",
	)
}

// resources_css_site_css_ reads file data from disk. It returns an error on failure.
func resources_css_site_css_() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/css/site.css~",
		"resources/css/site.css~",
	)
}

// resources_js_gladder_js_ reads file data from disk. It returns an error on failure.
func resources_js_gladder_js_() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/js/gladder.js~",
		"resources/js/gladder.js~",
	)
}

// resources_js_gladder_js reads file data from disk. It returns an error on failure.
func resources_js_gladder_js() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/js/gladder.js",
		"resources/js/gladder.js",
	)
}

// resources_js_jquery_2_1_1_min_js reads file data from disk. It returns an error on failure.
func resources_js_jquery_2_1_1_min_js() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/js/jquery-2.1.1.min.js",
		"resources/js/jquery-2.1.1.min.js",
	)
}

// resources_js_mustache_js reads file data from disk. It returns an error on failure.
func resources_js_mustache_js() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/js/mustache.js",
		"resources/js/mustache.js",
	)
}

// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
	cannonicalName := strings.Replace(name, "\\", "/", -1)
	if f, ok := _bindata[cannonicalName]; ok {
		return f()
	}
	return nil, fmt.Errorf("Asset %s not found", name)
}

// AssetNames returns the names of the assets.
func AssetNames() []string {
	names := make([]string, 0, len(_bindata))
	for name := range _bindata {
		names = append(names, name)
	}
	return names
}

// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() ([]byte, error){
	"resources/templates/create_user.html":  resources_templates_create_user_html,
	"resources/templates/index.html":        resources_templates_index_html,
	"resources/templates/create_user.html~": resources_templates_create_user_html_,
	"resources/templates/index.html~":       resources_templates_index_html_,
	"resources/css/site.css":                resources_css_site_css,
	"resources/css/site.css~":               resources_css_site_css_,
	"resources/js/gladder.js~":              resources_js_gladder_js_,
	"resources/js/gladder.js":               resources_js_gladder_js,
	"resources/js/jquery-2.1.1.min.js":      resources_js_jquery_2_1_1_min_js,
	"resources/js/mustache.js":              resources_js_mustache_js,
}

// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
//     data/
//       foo.txt
//       img/
//         a.png
//         b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
	node := _bintree
	if len(name) != 0 {
		cannonicalName := strings.Replace(name, "\\", "/", -1)
		pathList := strings.Split(cannonicalName, "/")
		for _, p := range pathList {
			node = node.Children[p]
			if node == nil {
				return nil, fmt.Errorf("Asset %s not found", name)
			}
		}
	}
	if node.Func != nil {
		return nil, fmt.Errorf("Asset %s not found", name)
	}
	rv := make([]string, 0, len(node.Children))
	for name := range node.Children {
		rv = append(rv, name)
	}
	return rv, nil
}

type _bintree_t struct {
	Func     func() ([]byte, error)
	Children map[string]*_bintree_t
}

var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
	"resources": &_bintree_t{nil, map[string]*_bintree_t{
		"templates": &_bintree_t{nil, map[string]*_bintree_t{
			"create_user.html":  &_bintree_t{resources_templates_create_user_html, map[string]*_bintree_t{}},
			"index.html":        &_bintree_t{resources_templates_index_html, map[string]*_bintree_t{}},
			"create_user.html~": &_bintree_t{resources_templates_create_user_html_, map[string]*_bintree_t{}},
			"index.html~":       &_bintree_t{resources_templates_index_html_, map[string]*_bintree_t{}},
		}},
		"css": &_bintree_t{nil, map[string]*_bintree_t{
			"site.css":  &_bintree_t{resources_css_site_css, map[string]*_bintree_t{}},
			"site.css~": &_bintree_t{resources_css_site_css_, map[string]*_bintree_t{}},
		}},
		"js": &_bintree_t{nil, map[string]*_bintree_t{
			"gladder.js~":         &_bintree_t{resources_js_gladder_js_, map[string]*_bintree_t{}},
			"gladder.js":          &_bintree_t{resources_js_gladder_js, map[string]*_bintree_t{}},
			"jquery-2.1.1.min.js": &_bintree_t{resources_js_jquery_2_1_1_min_js, map[string]*_bintree_t{}},
			"mustache.js":         &_bintree_t{resources_js_mustache_js, map[string]*_bintree_t{}},
		}},
	}},
}}
<commit_msg>remove temporary files from resources<commit_after>package main

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

// bindata_read reads the given file from disk. It returns an error on failure.
func bindata_read(path, name string) ([]byte, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		err = fmt.Errorf("Error reading asset %s at %s: %v", name, path, err)
	}
	return buf, err
}

// resources_templates_create_user_html reads file data from disk. It returns an error on failure.
func resources_templates_create_user_html() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/templates/create_user.html",
		"resources/templates/create_user.html",
	)
}

// resources_templates_index_html reads file data from disk. It returns an error on failure.
func resources_templates_index_html() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/templates/index.html",
		"resources/templates/index.html",
	)
}

// resources_css_site_css reads file data from disk. It returns an error on failure.
func resources_css_site_css() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/css/site.css",
		"resources/css/site.css",
	)
}

// resources_js_gladder_js reads file data from disk. It returns an error on failure.
func resources_js_gladder_js() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/js/gladder.js",
		"resources/js/gladder.js",
	)
}

// resources_js_jquery_2_1_1_min_js reads file data from disk. It returns an error on failure.
func resources_js_jquery_2_1_1_min_js() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/js/jquery-2.1.1.min.js",
		"resources/js/jquery-2.1.1.min.js",
	)
}

// resources_js_mustache_js reads file data from disk. It returns an error on failure.
func resources_js_mustache_js() ([]byte, error) {
	return bindata_read(
		"/home/aaron/stuff/go/src/gladder/resources/js/mustache.js",
		"resources/js/mustache.js",
	)
}

// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
	cannonicalName := strings.Replace(name, "\\", "/", -1)
	if f, ok := _bindata[cannonicalName]; ok {
		return f()
	}
	return nil, fmt.Errorf("Asset %s not found", name)
}

// AssetNames returns the names of the assets.
func AssetNames() []string {
	names := make([]string, 0, len(_bindata))
	for name := range _bindata {
		names = append(names, name)
	}
	return names
}

// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() ([]byte, error){
	"resources/templates/create_user.html": resources_templates_create_user_html,
	"resources/templates/index.html": resources_templates_index_html,
	"resources/css/site.css": resources_css_site_css,
	"resources/js/gladder.js": resources_js_gladder_js,
	"resources/js/jquery-2.1.1.min.js": resources_js_jquery_2_1_1_min_js,
	"resources/js/mustache.js": resources_js_mustache_js,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
//     data/
//       foo.txt
//       img/
//         a.png
//         b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
	node := _bintree
	if len(name) != 0 {
		cannonicalName := strings.Replace(name, "\\", "/", -1)
		pathList := strings.Split(cannonicalName, "/")
		for _, p := range pathList {
			node = node.Children[p]
			if node == nil {
				return nil, fmt.Errorf("Asset %s not found", name)
			}
		}
	}
	if node.Func != nil {
		return nil, fmt.Errorf("Asset %s not found", name)
	}
	rv := make([]string, 0, len(node.Children))
	for name := range node.Children {
		rv = append(rv, name)
	}
	return rv, nil
}

type _bintree_t struct {
	Func func() ([]byte, error)
	Children map[string]*_bintree_t
}
var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
	"resources": &_bintree_t{nil, map[string]*_bintree_t{
		"templates": &_bintree_t{nil, map[string]*_bintree_t{
			"create_user.html": &_bintree_t{resources_templates_create_user_html, map[string]*_bintree_t{
			}},
			"index.html": &_bintree_t{resources_templates_index_html, map[string]*_bintree_t{
			}},
		}},
		"css": &_bintree_t{nil, map[string]*_bintree_t{
			"site.css": &_bintree_t{resources_css_site_css, map[string]*_bintree_t{
			}},
		}},
		"js": &_bintree_t{nil, map[string]*_bintree_t{
			"gladder.js": &_bintree_t{resources_js_gladder_js, map[string]*_bintree_t{
			}},
			"jquery-2.1.1.min.js": &_bintree_t{resources_js_jquery_2_1_1_min_js, map[string]*_bintree_t{
			}},
			"mustache.js": &_bintree_t{resources_js_mustache_js, map[string]*_bintree_t{
			}},
		}},
	}},
}}
<|endoftext|>


<commit_before>package gorp

import (
	"log"
	"os"
	"time"

	"github.com/google/uuid"
)

func ExampleClient() {
	client := NewClient("xxx", "xxx", "xxx")

	launchUUID := uuid.New()
	launch, err := client.StartLaunch(&StartLaunchRQ{
		Mode: LaunchModes.Default,
		StartRQ: StartRQ{
			Name:        "gorp-test",
			UUID:        &launchUUID,
			StartTime:   Timestamp{Time: time.Now()},
			Description: "Demo Launch",
		},
	})
	checkErr(err, "unable to start launch")

	testUUID := uuid.New()
	_, err = client.StartTest(&StartTestRQ{
		LaunchID: launch.ID,
		CodeRef:  "example_test.go",
		UniqueID: "another one unique ID",
		Retry:    false,
		Type:     TestItemTypes.Test,
		StartRQ: StartRQ{
			Name:      "Gorp Test",
			StartTime: Timestamp{time.Now()},
			UUID:      &testUUID,
		},
	})
	checkErr(err, "unable to start test")

	log.Println("LAUNCHXXX")
	log.Println(launchUUID.String())

	_, err = client.SaveLog(&SaveLogRQ{
		LaunchUUID: launchUUID.String(),
		ItemID:     testUUID.String(),
		Level:      LogLevelInfo,
		LogTime:    Timestamp{time.Now()},
		Message:    "Log without binary",
	})
	checkErr(err, "unable to save log")

	file, _ := os.Open("../go.mod")
	_, err = client.SaveLogMultipart(&SaveLogRQ{
		LaunchUUID: launchUUID.String(),
		ItemID:     testUUID.String(),
		Level:      LogLevelInfo,
		Message:    "Log with binary",
	}, map[string]*os.File{"go.mod": file})

	checkErr(err, "unable to save log multipart")

	_, err = client.FinishTest(testUUID.String(), &FinishTestRQ{
		LaunchUUID: launchUUID.String(),
		FinishExecutionRQ: FinishExecutionRQ{
			EndTime: Timestamp{time.Now()},
			Status:  Statuses.Passed,
		},
	})
	checkErr(err, "unable to finish test")

	_, err = client.FinishLaunch(launchUUID.String(), &FinishExecutionRQ{
		Status:  Statuses.Passed,
		EndTime: Timestamp{time.Now()},
	})
	checkErr(err, "unable to finish launch")

}

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatal(msg, err)
	}
}
<commit_msg>fix formatting issues<commit_after>package gorp

import (
	"log"
	"os"
	"time"

	"github.com/google/uuid"
)

func ExampleClient() {
	client := NewClient("xxx", "xxx", "xxx")

	launchUUID := uuid.New()
	launch, err := client.StartLaunch(&StartLaunchRQ{
		Mode: LaunchModes.Default,
		StartRQ: StartRQ{
			Name:        "gorp-test",
			UUID:        &launchUUID,
			StartTime:   Timestamp{Time: time.Now()},
			Description: "Demo Launch",
		},
	})
	checkErr(err, "unable to start launch")

	testUUID := uuid.New()
	_, err = client.StartTest(&StartTestRQ{
		LaunchID: launch.ID,
		CodeRef:  "example_test.go",
		UniqueID: "another one unique ID",
		Retry:    false,
		Type:     TestItemTypes.Test,
		StartRQ: StartRQ{
			Name:      "Gorp Test",
			StartTime: Timestamp{time.Now()},
			UUID:      &testUUID,
		},
	})
	checkErr(err, "unable to start test")

	log.Println("LAUNCHXXX")
	log.Println(launchUUID.String())

	_, err = client.SaveLog(&SaveLogRQ{
		LaunchUUID: launchUUID.String(),
		ItemID:     testUUID.String(),
		Level:      LogLevelInfo,
		LogTime:    Timestamp{time.Now()},
		Message:    "Log without binary",
	})
	checkErr(err, "unable to save log")

	file, _ := os.Open("../go.mod")
	_, err = client.SaveLogMultipart(&SaveLogRQ{
		LaunchUUID: launchUUID.String(),
		ItemID:     testUUID.String(),
		Level:      LogLevelInfo,
		Message:    "Log with binary",
	}, map[string]*os.File{"go.mod": file})

	checkErr(err, "unable to save log multipart")

	_, err = client.FinishTest(testUUID.String(), &FinishTestRQ{
		LaunchUUID: launchUUID.String(),
		FinishExecutionRQ: FinishExecutionRQ{
			EndTime: Timestamp{time.Now()},
			Status:  Statuses.Passed,
		},
	})
	checkErr(err, "unable to finish test")

	_, err = client.FinishLaunch(launchUUID.String(), &FinishExecutionRQ{
		Status:  Statuses.Passed,
		EndTime: Timestamp{time.Now()},
	})
	checkErr(err, "unable to finish launch")
}

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatal(msg, err)
	}
}
<|endoftext|>


<commit_before>package gosql

import (
	"database/sql"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var cachedTx *sql.Tx

func cleanup() {
	if cachedTx != nil {
		cachedTx.Rollback()
		cachedTx = nil
	}
}

func testTx(t *testing.T) *sql.Tx {
	if cachedTx != nil {
		return cachedTx
	}
	e := func() error {
		db := testDb(t)
		var e error
		cachedTx, e = db.Begin()
		return e
	}()
	if e != nil {
		t.Fatal(e.Error())
	}
	return cachedTx
}

func prepareTx(t *testing.T, stmts ...string) *sql.Tx {
	tx := testTx(t)
	e := func() error {
		for _, s := range stmts {
			_, e := tx.Exec(s)
			if e != nil {
				return e
			}
		}
		return nil
	}()
	if e != nil {
		t.Fatal(e)
	}
	return tx
}

func TestSelect(t *testing.T) {

	type user struct {
		Id   int    `sql:"id"`
		Name string `sql:"name"`
	}

	Convey("Select struct", t, func() {
		tx := prepareTx(t, "CREATE TABLE users (id integer, name varchar)", "INSERT INTO users (id, name) VALUES (1, 'hans'), (11, 'marek')")
		u := &user{}
		e := SelectStruct(tx, "SELECT id, name FROM users ORDER BY id", u)
		So(e, ShouldBeNil)
		So(u.Name, ShouldEqual, "hans")
		So(u.Id, ShouldEqual, 1)

		var users []*user
		e = SelectStructs(tx, "SELECT id, name FROM users ORDER BY id", &users)
		So(e, ShouldBeNil)
		So(len(users), ShouldEqual, 2)
		So(users[1].Name, ShouldEqual, "marek")
		So(users[1].Id, ShouldEqual, 11)
	})

	Convey("SelectInt", t, func() {
		defer cleanup()
		tx := testTx(t)
		_, e := tx.Exec("CREATE TABLE ints (id INTEGER)")
		So(e, ShouldBeNil)

		_, e = tx.Exec("INSERT INTO ints VALUES (77), (88)")
		So(e, ShouldBeNil)

		cnt, e := SelectInt(tx, "SELECT id FROM ints ORDER BY id")
		So(e, ShouldBeNil)
		So(cnt, ShouldEqual, 77)

		ints, e := SelectInts(tx, "SELECT id FROM ints ORDER BY id")
		So(e, ShouldBeNil)
		So(len(ints), ShouldEqual, 2)
		So(ints[0], ShouldEqual, 77)
		So(ints[1], ShouldEqual, 88)
	})

	Convey("SelectString", t, func() {
		tx := prepareTx(t, "CREATE TABLE names (name VARCHAR)", "INSERT INTO names (name) VALUES ('hans'), ('meyer')")
		name, e := SelectString(tx, "SELECT name FROM names ORDER BY name")
		So(e, ShouldBeNil)
		So(name, ShouldEqual, "hans")

		names, e := SelectStrings(tx, "SELECT name FROM names ORDER BY name")
		So(e, ShouldBeNil)
		So(len(names), ShouldEqual, 2)
		So(names[0], ShouldEqual, "hans")
		So(names[1], ShouldEqual, "meyer")
	})

	Convey("ScanMap", t, func() {
		db := testDb(t)
		tx, e := db.Begin()
		So(e, ShouldBeNil)
		defer tx.Rollback()

		_, e = tx.Exec("CREATE TABLE users (id SERIAL NOT NULL, name VARCHAR NOT NULL)")
		So(e, ShouldBeNil)

		for _, n := range []string{"Marek Mintal", "Hans Meyer"} {
			_, e = tx.Exec("INSERT INTO users (name) VALUES ($1)", n)
			So(e, ShouldBeNil)
		}

		m := map[string]interface{}{}
		rows, e := tx.Query("SELECT * from users ORDER BY id")
		So(e, ShouldBeNil)
		rows.Next()

		e = ScanMap(rows, m)
		So(e, ShouldBeNil)
		So(len(m), ShouldEqual, 2)
		So(m["name"], ShouldEqual, "Marek Mintal")
	})

}
<commit_msg>refactor tests<commit_after>package gosql

import (
	"database/sql"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var cachedTx *sql.Tx

func cleanup() {
	if cachedTx != nil {
		cachedTx.Rollback()
		cachedTx = nil
	}
}

func testTx(t *testing.T) *sql.Tx {
	if cachedTx != nil {
		return cachedTx
	}
	e := func() error {
		db := testDb(t)
		var e error
		cachedTx, e = db.Begin()
		return e
	}()
	if e != nil {
		t.Fatal(e.Error())
	}
	return cachedTx
}

func prepareTx(t *testing.T, stmts ...string) *sql.Tx {
	tx := testTx(t)
	e := func() error {
		for _, s := range stmts {
			_, e := tx.Exec(s)
			if e != nil {
				return e
			}
		}
		return nil
	}()
	if e != nil {
		t.Fatal(e)
	}
	return tx
}

func TestSelect(t *testing.T) {

	type user struct {
		Id   int    `sql:"id"`
		Name string `sql:"name"`
	}

	Convey("Select struct", t, func() {
		defer cleanup()
		tx := prepareTx(t, "CREATE TABLE users (id integer, name varchar)", "INSERT INTO users (id, name) VALUES (1, 'hans'), (11, 'marek')")
		u := &user{}
		e := SelectStruct(tx, "SELECT id, name FROM users ORDER BY id", u)
		So(e, ShouldBeNil)
		So(u.Name, ShouldEqual, "hans")
		So(u.Id, ShouldEqual, 1)

		var users []*user
		e = SelectStructs(tx, "SELECT id, name FROM users ORDER BY id", &users)
		So(e, ShouldBeNil)
		So(len(users), ShouldEqual, 2)
		So(users[1].Name, ShouldEqual, "marek")
		So(users[1].Id, ShouldEqual, 11)
	})

	Convey("SelectInt", t, func() {
		defer cleanup()
		tx := prepareTx(t, "CREATE TABLE ints (id INTEGER)", "INSERT INTO ints VALUES (77), (88)")
		cnt, e := SelectInt(tx, "SELECT id FROM ints ORDER BY id")
		So(e, ShouldBeNil)
		So(cnt, ShouldEqual, 77)

		ints, e := SelectInts(tx, "SELECT id FROM ints ORDER BY id")
		So(e, ShouldBeNil)
		So(len(ints), ShouldEqual, 2)
		So(ints[0], ShouldEqual, 77)
		So(ints[1], ShouldEqual, 88)
	})

	Convey("SelectString", t, func() {
		tx := prepareTx(t, "CREATE TABLE names (name VARCHAR)", "INSERT INTO names (name) VALUES ('hans'), ('meyer')")
		name, e := SelectString(tx, "SELECT name FROM names ORDER BY name")
		So(e, ShouldBeNil)
		So(name, ShouldEqual, "hans")

		names, e := SelectStrings(tx, "SELECT name FROM names ORDER BY name")
		So(e, ShouldBeNil)
		So(len(names), ShouldEqual, 2)
		So(names[0], ShouldEqual, "hans")
		So(names[1], ShouldEqual, "meyer")
	})

	Convey("ScanMap", t, func() {
		tx := prepareTx(t, "CREATE TABLE users (id SERIAL NOT NULL, name VARCHAR NOT NULL)", "INSERT INTO users (name) VALUES ('Marek Mintal'), ('Hans Meyer')")

		m := map[string]interface{}{}
		rows, e := tx.Query("SELECT * from users ORDER BY id")
		So(e, ShouldBeNil)
		rows.Next()

		e = ScanMap(rows, m)
		So(e, ShouldBeNil)
		So(len(m), ShouldEqual, 2)
		So(m["name"], ShouldEqual, "Marek Mintal")
	})

}
<|endoftext|>