<commit_before>package main

import "fmt"
import "sync"
//import "time"

const MINHEAT = 10
const MEDIUMHEAT = 20
const MAXHEAT = 30

type tile struct {
	xCoord int
	yCoord int

	heat      int //how hot a tile is before fire
	fireLevel int //strength of the fire

	wall bool
	door bool

	occupied *Person
	personID int

	outOfBounds bool

	neighborNorth *tile
	neighborEast  *tile
	neighborSouth *tile
	neighborWest  *tile

	neighborNW *tile
	neighborNE *tile
	neighborSE *tile
	neighborSW *tile
}

//Initializes the fire
func SetFire(thisTile *tile) {
	thisTile.heat = MINHEAT
	thisTile.fireLevel = 1
}

func FireSpread(tileMap [][]tile) {
	for x := 0; x < len(tileMap); x++ {
		for y := 0; y < len(tileMap[0]); y++ {
			fireSpreadTile(&(tileMap[x][y]))
		}
	}

}

func fireSpreadTile(thisTile *tile) {
	if thisTile.heat >= MINHEAT {
		thisTile.fireLevel = 1
	}
	if thisTile.heat >= MEDIUMHEAT {
		thisTile.fireLevel = 2
	}
	if thisTile.heat >= MAXHEAT {
		thisTile.fireLevel = 3
	}

	if thisTile.neighborNorth != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborNorth.heat) += thisTile.fireLevel
	}
	if thisTile.neighborEast != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborEast.heat) += thisTile.fireLevel
	}
	if thisTile.neighborWest != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborWest.heat) += thisTile.fireLevel
	}
	if thisTile.neighborSouth != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborSouth.heat) += thisTile.fireLevel
	}
}

func assignNeighbor(thisTile *tile, x int, y int, maxX int, maxY int, tileMap [][]tile) {
	if x > 0 {
		thisTile.neighborNorth = &tileMap[x-1][y]
	}

	if y > 0 {
		thisTile.neighborWest = &tileMap[x][y-1]
	}

	if x < maxX-1 {
		thisTile.neighborSouth = &tileMap[x+1][y]
	}

	if y < maxY-1 {
		thisTile.neighborEast = &tileMap[x][y+1]
	}

	if x > 0 && y > 0 {
		thisTile.neighborNW = &tileMap[x-1][y-1]
	}
	if x > 0 && y < maxY-1 {
		thisTile.neighborNE = &tileMap[x-1][y+1]
	}
	if x < maxX-1 && y < maxY-1 {
		thisTile.neighborSE = &tileMap[x+1][y+1]
	}
	if x < maxX-1 && y > 0 {
		thisTile.neighborSW = &tileMap[x+1][y-1]
	}
}

func makeNewTile(thisPoint int, x int, y int) tile {

	//makes a basic floor tile with no nothin on it
	//and also no neighbors
	newTile := tile{x, y, 0, 0, false, false, nil, 0, false, nil, nil, nil, nil, nil, nil, nil, nil}

	if thisPoint == 0 {
		//make normal floor
		//helt normalt flour

		//append to tilemap
	} else if thisPoint == 1 {
		//wall
		newTile.wall = true
	} else if thisPoint == 2 {
		//door
		newTile.door = true
	} else if thisPoint == 3 {
		//out of bounds
		newTile.outOfBounds = true
	}

	return newTile
}

func TileConvert(inMap [][]int) [][]tile {
	mapXSize := len(inMap)
	mapYSize := len(inMap[0])

	//Initiates a slice of tile slices (2D tile slice)
	tileMap := make([][]tile, mapXSize)

	for x := 0; x < mapXSize; x++ {
		//initiates slice of tiles
		tileMap[x] = make([]tile, mapYSize)

		for y := 0; y < mapYSize; y++ {
			//constructs a new tile
			newTile := makeNewTile(inMap[x][y], x, y)

			//inserts tile into 2d slice
			tileMap[x][y] = newTile

		}
	}

	//Assigns 4 neighbors to each tile
	for x := 0; x < mapXSize; x++ {
		for y := 0; y < mapYSize; y++ {
			assignNeighbor(&(tileMap[x][y]), x, y, mapXSize, mapYSize, tileMap)
		}
	}

	return tileMap

}

func GetTile(inMap [][]tile, x int, y int) *tile {
	for i := range inMap {
		for j := range inMap[i] {
			if inMap[i][j].xCoord == x && inMap[i][j].yCoord == y {
				return &inMap[i][j]
			}
		}
	}
	return nil
}

func PeopleInit(inMap [][]tile, peopleList [][]int) []*Person {
	size := len(peopleList)
	peopleArray := make([]*Person, size)
	for i, person := range peopleList {
		tile := GetTile(inMap, person[0], person[1])
		peopleArray[i] = makePerson(tile)
	}
	return peopleArray
}

func Run(m *[][]tile, ppl []*Person, statsList *[][]int) {
	//sList := []Stats{}

	var wg sync.WaitGroup

	wg.Add(len(ppl))
	*statsList = [][]int{}
	for _, pers := range ppl {

		go func(p *Person){//, ind int){
			//	ind := i
			//p := pers
			defer wg.Done()

			//	sList :=  []int{}// append(sList, p.getStats())
			p.MovePerson(m)
			sList := &[]int{}
			p.getStats(sList)//(statsList[ind])
			*statsList = append(*statsList, *sList)
			//	fmt.Println(len(statsList))

		}(pers)//, i)
	}
	step++
	wg.Wait()
	FireSpread(*m)
	//	}

/*	// go run ruitnes for concurrency
	for _, person := range peopleArray {
		person.MovePerson(&inMap)
	}*/
//	return sList
}

func RunGo(inMap *[][]tile, peopleArray []*Person) []*tile{   // OBS: not working
	movement := make([]*tile, len(peopleArray))
	var wg sync.WaitGroup

	wg.Add(len(peopleArray))
	for i, person := range peopleArray {
		go func(currentPerson *Person, ind int) {
			defer wg.Done()
			if (!currentPerson.DiagonalStep()) {
				fmt.Println("not diagonal move!")
				currentPerson.MovePerson(inMap)}
			if currentPerson.IsWaiting() {
				movement[ind] = nil
			} else {
				movement[ind] = currentPerson.path[len(currentPerson.path) - 1]}
		}(person, i)
	}
	return movement
}

 func CheckFinish (peopleArray []*Person) bool {
	for i := 0; i < len(peopleArray); i++ {
		if (peopleArray[i].safe == false && peopleArray[i].alive == true) {
			return false
		}
	}
	return true
 }

func mainMap() {
//	mainPath()
//	MainPeople()
//      testRedirect()
//	testDiagPpl()
//	testDiag()
//	testDiagonally()
	testMovePeople()
}

func testRedirect() {
	matrix := [][]int{
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{1, 0, 1, 1, 1, 0, 1},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 2, 0, 0, 0, 0}}

	list := [][]int{
		{1, 1},
		{1, 2},
		{0, 2},
		{0, 0},
		{2, 1},
		{3, 1},
		{0, 3},
		{0, 1},
		{1, 0},
		{1, 3}}

	tryThis(matrix, list, -1, -1)
}

func testDiag() {
	matrix := [][]int{
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 6}}
	tryThis(matrix, list, -1, -1)
}

func testDiagPpl() {
	matrix := [][]int{
		{0, 0, 1, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{1, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 1},
		{1, 0},
		{0, 6},
		{2, 4}}

	tryThis(matrix, list, -1, -1)
}

func testDiagonally() {
	matrix := [][]int{
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 1, 0, 0, 0},
		{0, 1, 0, 1, 0, 0, 0},
		{0, 0, 0, 1, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 6},
		{2, 4}}

	tryThis(matrix, list, 1, 2)
}

func testMovePeople() {
	matrix := [][]int{
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 6},
		{2, 4}}

	tryThis(matrix, list, -1, -1)
	// Note: it takes 1 timeunit to take a step from the door and away
}

func tryThis(matrix [][]int, ppl [][]int, x, y int) {
	testmap := TileConvert(matrix)
	pplArray := PeopleInit(testmap, ppl)

	if x >= 0 && y >= 0 {SetFire(&testmap[x][y])}
	MovePeople(&testmap, pplArray)

	for i, p := range pplArray {
		fmt.Println("Person", i, "time:  ", p.time, "\n         health:", p.hp)
	}
}

func CompileStats(peopleArray []*Person) []int {

	aliveAmount := 0
	deadAmount := 0
	injuredAmount := 0

	/*
	statSlice := make([][]int, len(peopleArray))
	//movement := make([]*tile, len(peopleArray))
	for i = 0; i < len(peopleArray); i++ {
		[i]statSlice := make([]int, )
	}
	*/

	for i := 0; i < len(peopleArray); i++ {
		if ((peopleArray[i]).alive) && ((peopleArray[i]).hp < 70) {
			injuredAmount++
		} else if (peopleArray[i].alive) {
			aliveAmount++
		} else {
			deadAmount++
		}
	}

	return []int{aliveAmount, deadAmount, injuredAmount}
}
/*
func main() {
	//compileStats()
	fmt.Print("hej")
}
*/
<commit_msg>fixed comment mistake<commit_after>package main

import "fmt"
import "sync"
//import "time"

const MINHEAT = 10
const MEDIUMHEAT = 20
const MAXHEAT = 30

type tile struct {
	xCoord int
	yCoord int

	heat      int //how hot a tile is before fire
	fireLevel int //strength of the fire

	wall bool
	door bool

	occupied *Person
	personID int

	outOfBounds bool

	neighborNorth *tile
	neighborEast  *tile
	neighborSouth *tile
	neighborWest  *tile

	neighborNW *tile
	neighborNE *tile
	neighborSE *tile
	neighborSW *tile
}

//Initializes the fire
func SetFire(thisTile *tile) {
	thisTile.heat = MINHEAT
	thisTile.fireLevel = 1
}

func FireSpread(tileMap [][]tile) {
	for x := 0; x < len(tileMap); x++ {
		for y := 0; y < len(tileMap[0]); y++ {
			fireSpreadTile(&(tileMap[x][y]))
		}
	}

}

func fireSpreadTile(thisTile *tile) {
	if thisTile.heat >= MINHEAT {
		thisTile.fireLevel = 1
	}
	if thisTile.heat >= MEDIUMHEAT {
		thisTile.fireLevel = 2
	}
	if thisTile.heat >= MAXHEAT {
		thisTile.fireLevel = 3
	}

	if thisTile.neighborNorth != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborNorth.heat) += thisTile.fireLevel
	}
	if thisTile.neighborEast != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborEast.heat) += thisTile.fireLevel
	}
	if thisTile.neighborWest != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborWest.heat) += thisTile.fireLevel
	}
	if thisTile.neighborSouth != nil && thisTile.fireLevel != 0 {
		(thisTile.neighborSouth.heat) += thisTile.fireLevel
	}
}

func assignNeighbor(thisTile *tile, x int, y int, maxX int, maxY int, tileMap [][]tile) {
	if x > 0 {
		thisTile.neighborNorth = &tileMap[x-1][y]
	}

	if y > 0 {
		thisTile.neighborWest = &tileMap[x][y-1]
	}

	if x < maxX-1 {
		thisTile.neighborSouth = &tileMap[x+1][y]
	}

	if y < maxY-1 {
		thisTile.neighborEast = &tileMap[x][y+1]
	}

	if x > 0 && y > 0 {
		thisTile.neighborNW = &tileMap[x-1][y-1]
	}
	if x > 0 && y < maxY-1 {
		thisTile.neighborNE = &tileMap[x-1][y+1]
	}
	if x < maxX-1 && y < maxY-1 {
		thisTile.neighborSE = &tileMap[x+1][y+1]
	}
	if x < maxX-1 && y > 0 {
		thisTile.neighborSW = &tileMap[x+1][y-1]
	}
}

func makeNewTile(thisPoint int, x int, y int) tile {

	//makes a basic floor tile with no nothin on it
	//and also no neighbors
	newTile := tile{x, y, 0, 0, false, false, nil, 0, false, nil, nil, nil, nil, nil, nil, nil, nil}

	if thisPoint == 0 {
		//make normal floor
		//helt normalt flour

		//append to tilemap
	} else if thisPoint == 1 {
		//wall
		newTile.wall = true
	} else if thisPoint == 2 {
		//door
		newTile.door = true
	} else if thisPoint == 3 {
		//out of bounds
		newTile.outOfBounds = true
	}

	return newTile
}

func TileConvert(inMap [][]int) [][]tile {
	mapXSize := len(inMap)
	mapYSize := len(inMap[0])

	//Initiates a slice of tile slices (2D tile slice)
	tileMap := make([][]tile, mapXSize)

	for x := 0; x < mapXSize; x++ {
		//initiates slice of tiles
		tileMap[x] = make([]tile, mapYSize)

		for y := 0; y < mapYSize; y++ {
			//constructs a new tile
			newTile := makeNewTile(inMap[x][y], x, y)

			//inserts tile into 2d slice
			tileMap[x][y] = newTile

		}
	}

	//Assigns 4 neighbors to each tile
	for x := 0; x < mapXSize; x++ {
		for y := 0; y < mapYSize; y++ {
			assignNeighbor(&(tileMap[x][y]), x, y, mapXSize, mapYSize, tileMap)
		}
	}

	return tileMap

}

func GetTile(inMap [][]tile, x int, y int) *tile {
	for i := range inMap {
		for j := range inMap[i] {
			if inMap[i][j].xCoord == x && inMap[i][j].yCoord == y {
				return &inMap[i][j]
			}
		}
	}
	return nil
}

func PeopleInit(inMap [][]tile, peopleList [][]int) []*Person {
	size := len(peopleList)
	peopleArray := make([]*Person, size)
	for i, person := range peopleList {
		tile := GetTile(inMap, person[0], person[1])
		peopleArray[i] = makePerson(tile)
	}
	return peopleArray
}

func Run(m *[][]tile, ppl []*Person, statsList *[][]int) {
	//sList := []Stats{}

	var wg sync.WaitGroup

	wg.Add(len(ppl))
	*statsList = [][]int{}
	for _, pers := range ppl {

		go func(p *Person){//, ind int){
			//	ind := i
			//p := pers
			defer wg.Done()

			//	sList :=  []int{}// append(sList, p.getStats())
			p.MovePerson(m)
			sList := &[]int{}
			p.getStats(sList)//(statsList[ind])
			*statsList = append(*statsList, *sList)
			//	fmt.Println(len(statsList))

		}(pers)//, i)
	}
	step++
	wg.Wait()
	FireSpread(*m)
	//	}

/*	// go run ruitnes for concurrency
	for _, person := range peopleArray {
		person.MovePerson(&inMap)
	}*/
//	return sList
}

func RunGo(inMap *[][]tile, peopleArray []*Person) []*tile{   // OBS: not working
	movement := make([]*tile, len(peopleArray))
	var wg sync.WaitGroup

	wg.Add(len(peopleArray))
	for i, person := range peopleArray {
		go func(currentPerson *Person, ind int) {
			defer wg.Done()
			if (!currentPerson.DiagonalStep()) {
				fmt.Println("not diagonal move!")
				currentPerson.MovePerson(inMap)}
			if currentPerson.IsWaiting() {
				movement[ind] = nil
			} else {
				movement[ind] = currentPerson.path[len(currentPerson.path) - 1]}
		}(person, i)
	}
	return movement
}

 func CheckFinish (peopleArray []*Person) bool {
	for i := 0; i < len(peopleArray); i++ {
		if (peopleArray[i].safe == false && peopleArray[i].alive == true) {
			return false
		}
	}
	return true
 }

func mainMap() {
//	mainPath()
//	MainPeople()
//      testRedirect()
//	testDiagPpl()
//	testDiag()
//	testDiagonally()
	testMovePeople()
}

func testRedirect() {
	matrix := [][]int{
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{1, 0, 1, 1, 1, 0, 1},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 2, 0, 0, 0, 0}}

	list := [][]int{
		{1, 1},
		{1, 2},
		{0, 2},
		{0, 0},
		{2, 1},
		{3, 1},
		{0, 3},
		{0, 1},
		{1, 0},
		{1, 3}}

	tryThis(matrix, list, -1, -1)
}

func testDiag() {
	matrix := [][]int{
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 6}}
	tryThis(matrix, list, -1, -1)
}

func testDiagPpl() {
	matrix := [][]int{
		{0, 0, 1, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{1, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 1},
		{1, 0},
		{0, 6},
		{2, 4}}

	tryThis(matrix, list, -1, -1)
}

func testDiagonally() {
	matrix := [][]int{
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 0, 0, 0, 0},
		{0, 1, 0, 1, 0, 0, 0},
		{0, 1, 0, 1, 0, 0, 0},
		{0, 0, 0, 1, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 6},
		{2, 4}}

	tryThis(matrix, list, 1, 2)
}

func testMovePeople() {
	matrix := [][]int{
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 2}}

	list := [][]int{
		{0, 0},
		{0, 6},
		{2, 4}}

	tryThis(matrix, list, -1, -1)
	// Note: it takes 1 timeunit to take a step from the door and away
}

func tryThis(matrix [][]int, ppl [][]int, x, y int) {
	testmap := TileConvert(matrix)
	pplArray := PeopleInit(testmap, ppl)

	if x >= 0 && y >= 0 {SetFire(&testmap[x][y])}
	MovePeople(&testmap, pplArray)

	for i, p := range pplArray {
		fmt.Println("Person", i, "time:  ", p.time, "\n         health:", p.hp)
	}
}

func CompileStats(peopleArray []*Person) []int {

	aliveAmount := 0
	deadAmount := 0
	injuredAmount := 0

	for i := 0; i < len(peopleArray); i++ {
		if ((peopleArray[i]).alive) && ((peopleArray[i]).hp < 70) {
			injuredAmount++
		} else if (peopleArray[i].alive) {
			aliveAmount++
		} else {
			deadAmount++
		}
	}

	return []int{aliveAmount, deadAmount, injuredAmount}
}
<|endoftext|>


<commit_before>package hero

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/jinzhu/now"
	"github.com/pborman/uuid"
)

// SimpleTokenGen implements TokenGenerator interface
type SimpleTokenGen struct{}

// Generate returns a UUID v4 string
func (s *SimpleTokenGen) Generate() string {
	return uuid.NewRandom().String()
}

type JWTTokenGen struct {
	publicKey  []byte
	privateKey []byte
}

func (j *JWTTokenGen) Generate() string {
	exp := now.EndOfYear()
	token := jwt.New(jwt.SigningMethodRS256)
	token.Claims["exp"] = exp.Unix()
	tok, err := token.SignedString(j.privateKey)
	if err != nil {
		panic(err)
	}
	return tok
}
<commit_msg>Add JWtTokenGen initializer<commit_after>package hero

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/jinzhu/now"
	"github.com/pborman/uuid"
)

// SimpleTokenGen implements TokenGenerator interface
type SimpleTokenGen struct{}

// Generate returns a UUID v4 string
func (s *SimpleTokenGen) Generate() string {
	return uuid.NewRandom().String()
}

type JWTTokenGen struct {
	publicKey  []byte
	privateKey []byte
}

func NewJWTGen(public, private []byte) *JWTTokenGen {
	return &JWTTokenGen{publicKey: public, privateKey: private}
}

func (j *JWTTokenGen) Generate() string {
	exp := now.EndOfYear()
	token := jwt.New(jwt.SigningMethodRS256)
	token.Claims["exp"] = exp.Unix()
	tok, err := token.SignedString(j.privateKey)
	if err != nil {
		panic(err)
	}
	return tok
}
<|endoftext|>


<commit_before>package main

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

type Token struct {
	Token string `json:"token"`
}

func NewToken(user, pass string) (*Token, error) {
	req, err := http.NewRequest(
		"POST",
		"https://api.github.com/authorizations",
		strings.NewReader(`{"note":"gfm-viewer"}`),
	)
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(user, pass)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		body, _ := ioutil.ReadAll(res.Body)
		return nil, fmt.Errorf(string(body))
	}

	t := &Token{}
	json.NewDecoder(res.Body).Decode(t)

	return t, nil
}
<commit_msg>Token save method<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"strings"
)

type Token struct {
	Token string `json:"token"`
}

func NewToken(user, pass string) (*Token, error) {
	req, err := http.NewRequest(
		"POST",
		"https://api.github.com/authorizations",
		strings.NewReader(`{"note":"gfm-viewer"}`),
	)
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(user, pass)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		body, _ := ioutil.ReadAll(res.Body)
		return nil, fmt.Errorf(string(body))
	}

	t := &Token{}
	json.NewDecoder(res.Body).Decode(t)

	return t, nil
}

func (t *Token) Save() error {
	return ioutil.WriteFile(t.filePath(), []byte(t.Token), 0644)
}

func (_ *Token) filePath() string {
	fname := "gfm-viewer"
	if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
		return path.Join(xdg, fname)
	} else {
		return path.Join(os.Getenv("HOME"), ".cache", fname)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

// Token is the set of lexical tokens.
type Token int

// The list of all possible tokens.
const (
	ILLEGAL Token = iota
	STOPPED
	EOF
	LIT
	LITWORD
	COMMENT

	// Rest of tokens
	SQUOTE // '
	DQUOTE // "
	BQUOTE // `

	AND  // &
	LAND // &&
	OR   // |
	LOR  // ||

	ASSIGN // =
	DOLLAR // $
	DOLLSQ // $'
	DOLLDQ // $"
	DOLLBR // ${
	DOLLPR // $(
	DOLLDP // $((
	LET    // let
	LBRACE // {
	LPAREN // (

	RBRACE     // }
	RPAREN     // )
	SEMICOLON  // ;
	DSEMICOLON // ;;
	SEMIFALL   // ;&
	DSEMIFALL  // ;;&
	COLON      // :

	LSS // <
	GTR // >
	SHL // <<
	SHR // >>

	ADD   // +
	SUB   // -
	REM   // %
	MUL   // *
	QUO   // /
	XOR   // ^
	NOT   // !
	INC   // ++
	DEC   // --
	POW   // **
	COMMA // ,
	EQL   // ==
	NEQ   // !=
	LEQ   // <=
	GEQ   // >=

	ADDASSGN // +=
	SUBASSGN // -=
	MULASSGN // *=
	QUOASSGN // /=
	REMASSGN // %=
	ANDASSGN // &=
	ORASSGN  // |=
	XORASSGN // ^=
	SHLASSGN // <<=
	SHRASSGN // >>=

	PIPEALL  // |&
	RDRINOUT // <>
	DPLIN    // <&
	DPLOUT   // >&
	DHEREDOC // <<-
	WHEREDOC // <<<
	CMDIN    // <(
	CMDOUT   // >(
	RDRALL   // &>
	APPALL   // &>>

	CADD    // :+
	CSUB    // :-
	QUEST   // ?
	CQUEST  // :?
	CASSIGN // :=
	DREM    // %%
	HASH    // #
	DHASH   // ##
	LBRACK  // [
	RBRACK  // ]
	DQUO    // //

	DLPAREN // ((
	DRPAREN // ))
)

// Pos is the internal representation of a position within a source
// file.
type Pos struct {
	Line   int // line number, starting at 1
	Column int // column number (in bytes), starting at 1
}

func posGreater(p1, p2 Pos) bool {
	if p1.Line == p2.Line {
		return p1.Column > p2.Column
	}
	return p1.Line > p2.Line
}

func posMax(p1, p2 Pos) Pos {
	if posGreater(p2, p1) {
		return p2
	}
	return p1
}

var (
	tokNames = map[Token]string{
		ILLEGAL: `ILLEGAL`,
		STOPPED: `STOPPED`,
		EOF:     `EOF`,
		LIT:     `lit`,
		LITWORD: `litword`,
		COMMENT: `comment`,

		DLPAREN: "((",
		DRPAREN: "))",

		SQUOTE: `'`,
		DQUOTE: `"`,
		BQUOTE: "`",

		AND:  "&",
		LAND: "&&",
		OR:   "|",
		LOR:  "||",

		DOLLAR:     "$",
		DOLLSQ:     "$'",
		DOLLDQ:     `$"`,
		DOLLBR:     `${`,
		DOLLPR:     `$(`,
		DOLLDP:     `$((`,
		LET:        "let",
		LBRACE:     "{",
		LPAREN:     "(",
		RBRACE:     "}",
		RPAREN:     ")",
		SEMICOLON:  ";",
		DSEMICOLON: ";;",
		SEMIFALL:   ";&",
		DSEMIFALL:  ";;&",

		LSS:      "<",
		GTR:      ">",
		SHL:      "<<",
		SHR:      ">>",
		PIPEALL:  "|&",
		RDRINOUT: "<>",
		DPLIN:    "<&",
		DPLOUT:   ">&",
		DHEREDOC: "<<-",
		WHEREDOC: "<<<",
		CMDIN:    "<(",
		CMDOUT:   ">(",
		RDRALL:   "&>",
		APPALL:   "&>>",

		COLON:   ":",
		ADD:     "+",
		CADD:    ":+",
		SUB:     "-",
		CSUB:    ":-",
		QUEST:   "?",
		CQUEST:  ":?",
		ASSIGN:  "=",
		CASSIGN: ":=",
		REM:     "%",
		DREM:    "%%",
		HASH:    "#",
		DHASH:   "##",
		LBRACK:  "[",
		RBRACK:  "]",
		QUO:     "/",
		DQUO:    "//",

		MUL:   "*",
		XOR:   "^",
		NOT:   "!",
		INC:   "++",
		DEC:   "--",
		POW:   "**",
		COMMA: ",",
		EQL:   "==",
		NEQ:   "!=",
		LEQ:   "<=",
		GEQ:   ">=",

		ADDASSGN: "+=",
		SUBASSGN: "-=",
		MULASSGN: "*=",
		QUOASSGN: "/=",
		REMASSGN: "%=",
		ANDASSGN: "&=",
		ORASSGN:  "|=",
		XORASSGN: "^=",
		SHLASSGN: "<<=",
		SHRASSGN: ">>=",
	}
)

func (t Token) String() string { return tokNames[t] }

// TODO: decouple from parser. Passing readOnly as a func argument
// doesn't seem to work well as it means an extra allocation (?).
func (p *parser) doRegToken(b byte) Token {
	switch b {
	case '\'':
		return SQUOTE
	case '"':
		return DQUOTE
	case '`':
		return BQUOTE
	case '&':
		switch {
		case p.readOnly('&'):
			return LAND
		case p.readOnly('>'):
			if p.readOnly('>') {
				return APPALL
			}
			return RDRALL
		}
		return AND
	case '|':
		switch {
		case p.readOnly('|'):
			return LOR
		case p.readOnly('&'):
			return PIPEALL
		}
		return OR
	case '$':
		switch {
		case p.readOnly('\''):
			return DOLLSQ
		case p.readOnly('"'):
			return DOLLDQ
		case p.readOnly('{'):
			return DOLLBR
		case p.readOnly('('):
			if p.readOnly('(') {
				return DOLLDP
			}
			return DOLLPR
		}
		return DOLLAR
	case '(':
		return LPAREN
	case ')':
		return RPAREN
	case ';':
		if p.readOnly(';') {
			if p.readOnly('&') {
				return DSEMIFALL
			}
			return DSEMICOLON
		}
		if p.readOnly('&') {
			return SEMIFALL
		}
		return SEMICOLON
	case '<':
		switch {
		case p.readOnly('<'):
			if p.readOnly('-') {
				return DHEREDOC
			}
			if p.readOnly('<') {
				return WHEREDOC
			}
			return SHL
		case p.readOnly('>'):
			return RDRINOUT
		case p.readOnly('&'):
			return DPLIN
		case p.readOnly('('):
			return CMDIN
		}
		return LSS
	case '>':
		switch {
		case p.readOnly('>'):
			return SHR
		case p.readOnly('&'):
			return DPLOUT
		case p.readOnly('('):
			return CMDOUT
		}
		return GTR
	}
	return ILLEGAL
}

func (p *parser) doParamToken(b byte) Token {
	switch b {
	case ':':
		switch {
		case p.readOnly('+'):
			return CADD
		case p.readOnly('-'):
			return CSUB
		case p.readOnly('?'):
			return CQUEST
		case p.readOnly('='):
			return CASSIGN
		}
		return COLON
	case '+':
		return ADD
	case '-':
		return SUB
	case '?':
		return QUEST
	case '=':
		return ASSIGN
	case '%':
		if p.readOnly('%') {
			return DREM
		}
		return REM
	case '#':
		if p.readOnly('#') {
			return DHASH
		}
		return HASH
	case '[':
		return LBRACK
	case '/':
		if p.readOnly('/') {
			return DQUO
		}
		return QUO
	}
	return ILLEGAL
}

func (p *parser) doArithmToken(b byte) Token {
	switch b {
	case '!':
		if p.readOnly('=') {
			return NEQ
		}
		return NOT
	case '=':
		if p.readOnly('=') {
			return EQL
		}
		return ASSIGN
	case '(':
		return LPAREN
	case ')':
		return RPAREN
	case '&':
		if p.readOnly('&') {
			return LAND
		}
		if p.readOnly('=') {
			return ANDASSGN
		}
		return AND
	case '|':
		if p.readOnly('|') {
			return LOR
		}
		if p.readOnly('=') {
			return ORASSGN
		}
		return OR
	case '<':
		switch {
		case p.readOnly('<'):
			if p.readOnly('=') {
				return SHLASSGN
			}
			return SHL
		case p.readOnly('='):
			return LEQ
		}
		return LSS
	case '>':
		switch {
		case p.readOnly('>'):
			if p.readOnly('=') {
				return SHRASSGN
			}
			return SHR
		case p.readOnly('='):
			return GEQ
		}
		return GTR
	case '+':
		if p.readOnly('+') {
			return INC
		}
		if p.readOnly('=') {
			return ADDASSGN
		}
		return ADD
	case '-':
		if p.readOnly('-') {
			return DEC
		}
		if p.readOnly('=') {
			return SUBASSGN
		}
		return SUB
	case '%':
		if p.readOnly('=') {
			return REMASSGN
		}
		return REM
	case '*':
		if p.readOnly('*') {
			return POW
		}
		if p.readOnly('=') {
			return MULASSGN
		}
		return MUL
	case '/':
		if p.readOnly('=') {
			return QUOASSGN
		}
		return QUO
	case '^':
		if p.readOnly('=') {
			return XORASSGN
		}
		return XOR
	case ',':
		return COMMA
	case '?':
		return QUEST
	case ':':
		return COLON
	}
	return ILLEGAL
}
<commit_msg>token: back to 100% coverage<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

// Token is the set of lexical tokens.
type Token int

// The list of all possible tokens.
const (
	ILLEGAL Token = iota
	STOPPED
	EOF
	LIT
	LITWORD
	COMMENT

	// Rest of tokens
	SQUOTE // '
	DQUOTE // "
	BQUOTE // `

	AND  // &
	LAND // &&
	OR   // |
	LOR  // ||

	ASSIGN // =
	DOLLAR // $
	DOLLSQ // $'
	DOLLDQ // $"
	DOLLBR // ${
	DOLLPR // $(
	DOLLDP // $((
	LET    // let
	LBRACE // {
	LPAREN // (

	RBRACE     // }
	RPAREN     // )
	SEMICOLON  // ;
	DSEMICOLON // ;;
	SEMIFALL   // ;&
	DSEMIFALL  // ;;&
	COLON      // :

	LSS // <
	GTR // >
	SHL // <<
	SHR // >>

	ADD   // +
	SUB   // -
	REM   // %
	MUL   // *
	QUO   // /
	XOR   // ^
	NOT   // !
	INC   // ++
	DEC   // --
	POW   // **
	COMMA // ,
	EQL   // ==
	NEQ   // !=
	LEQ   // <=
	GEQ   // >=

	ADDASSGN // +=
	SUBASSGN // -=
	MULASSGN // *=
	QUOASSGN // /=
	REMASSGN // %=
	ANDASSGN // &=
	ORASSGN  // |=
	XORASSGN // ^=
	SHLASSGN // <<=
	SHRASSGN // >>=

	PIPEALL  // |&
	RDRINOUT // <>
	DPLIN    // <&
	DPLOUT   // >&
	DHEREDOC // <<-
	WHEREDOC // <<<
	CMDIN    // <(
	CMDOUT   // >(
	RDRALL   // &>
	APPALL   // &>>

	CADD    // :+
	CSUB    // :-
	QUEST   // ?
	CQUEST  // :?
	CASSIGN // :=
	DREM    // %%
	HASH    // #
	DHASH   // ##
	LBRACK  // [
	RBRACK  // ]
	DQUO    // //

	DLPAREN // ((
	DRPAREN // ))
)

// Pos is the internal representation of a position within a source
// file.
type Pos struct {
	Line   int // line number, starting at 1
	Column int // column number (in bytes), starting at 1
}

func posGreater(p1, p2 Pos) bool {
	if p1.Line == p2.Line {
		return p1.Column > p2.Column
	}
	return p1.Line > p2.Line
}

func posMax(p1, p2 Pos) Pos {
	if posGreater(p2, p1) {
		return p2
	}
	return p1
}

var (
	tokNames = map[Token]string{
		ILLEGAL: `ILLEGAL`,
		STOPPED: `STOPPED`,
		EOF:     `EOF`,
		LIT:     `lit`,
		LITWORD: `litword`,
		COMMENT: `comment`,

		DLPAREN: "((",
		DRPAREN: "))",

		SQUOTE: `'`,
		DQUOTE: `"`,
		BQUOTE: "`",

		AND:  "&",
		LAND: "&&",
		OR:   "|",
		LOR:  "||",

		DOLLAR:     "$",
		DOLLSQ:     "$'",
		DOLLDQ:     `$"`,
		DOLLBR:     `${`,
		DOLLPR:     `$(`,
		DOLLDP:     `$((`,
		LET:        "let",
		LBRACE:     "{",
		LPAREN:     "(",
		RBRACE:     "}",
		RPAREN:     ")",
		SEMICOLON:  ";",
		DSEMICOLON: ";;",
		SEMIFALL:   ";&",
		DSEMIFALL:  ";;&",

		LSS:      "<",
		GTR:      ">",
		SHL:      "<<",
		SHR:      ">>",
		PIPEALL:  "|&",
		RDRINOUT: "<>",
		DPLIN:    "<&",
		DPLOUT:   ">&",
		DHEREDOC: "<<-",
		WHEREDOC: "<<<",
		CMDIN:    "<(",
		CMDOUT:   ">(",
		RDRALL:   "&>",
		APPALL:   "&>>",

		COLON:   ":",
		ADD:     "+",
		CADD:    ":+",
		SUB:     "-",
		CSUB:    ":-",
		QUEST:   "?",
		CQUEST:  ":?",
		ASSIGN:  "=",
		CASSIGN: ":=",
		REM:     "%",
		DREM:    "%%",
		HASH:    "#",
		DHASH:   "##",
		LBRACK:  "[",
		RBRACK:  "]",
		QUO:     "/",
		DQUO:    "//",

		MUL:   "*",
		XOR:   "^",
		NOT:   "!",
		INC:   "++",
		DEC:   "--",
		POW:   "**",
		COMMA: ",",
		EQL:   "==",
		NEQ:   "!=",
		LEQ:   "<=",
		GEQ:   ">=",

		ADDASSGN: "+=",
		SUBASSGN: "-=",
		MULASSGN: "*=",
		QUOASSGN: "/=",
		REMASSGN: "%=",
		ANDASSGN: "&=",
		ORASSGN:  "|=",
		XORASSGN: "^=",
		SHLASSGN: "<<=",
		SHRASSGN: ">>=",
	}
)

func (t Token) String() string { return tokNames[t] }

// TODO: decouple from parser. Passing readOnly as a func argument
// doesn't seem to work well as it means an extra allocation (?).
func (p *parser) doRegToken(b byte) Token {
	switch b {
	case '\'':
		return SQUOTE
	case '"':
		return DQUOTE
	case '`':
		return BQUOTE
	case '&':
		switch {
		case p.readOnly('&'):
			return LAND
		case p.readOnly('>'):
			if p.readOnly('>') {
				return APPALL
			}
			return RDRALL
		}
		return AND
	case '|':
		switch {
		case p.readOnly('|'):
			return LOR
		case p.readOnly('&'):
			return PIPEALL
		}
		return OR
	case '$':
		switch {
		case p.readOnly('\''):
			return DOLLSQ
		case p.readOnly('"'):
			return DOLLDQ
		case p.readOnly('{'):
			return DOLLBR
		case p.readOnly('('):
			if p.readOnly('(') {
				return DOLLDP
			}
			return DOLLPR
		}
		return DOLLAR
	case '(':
		return LPAREN
	case ')':
		return RPAREN
	case ';':
		if p.readOnly(';') {
			if p.readOnly('&') {
				return DSEMIFALL
			}
			return DSEMICOLON
		}
		if p.readOnly('&') {
			return SEMIFALL
		}
		return SEMICOLON
	case '<':
		switch {
		case p.readOnly('<'):
			if p.readOnly('-') {
				return DHEREDOC
			}
			if p.readOnly('<') {
				return WHEREDOC
			}
			return SHL
		case p.readOnly('>'):
			return RDRINOUT
		case p.readOnly('&'):
			return DPLIN
		case p.readOnly('('):
			return CMDIN
		}
		return LSS
	default: // '>'
		switch {
		case p.readOnly('>'):
			return SHR
		case p.readOnly('&'):
			return DPLOUT
		case p.readOnly('('):
			return CMDOUT
		}
		return GTR
	}
}

func (p *parser) doParamToken(b byte) Token {
	switch b {
	case ':':
		switch {
		case p.readOnly('+'):
			return CADD
		case p.readOnly('-'):
			return CSUB
		case p.readOnly('?'):
			return CQUEST
		case p.readOnly('='):
			return CASSIGN
		}
		return COLON
	case '+':
		return ADD
	case '-':
		return SUB
	case '?':
		return QUEST
	case '=':
		return ASSIGN
	case '%':
		if p.readOnly('%') {
			return DREM
		}
		return REM
	case '#':
		if p.readOnly('#') {
			return DHASH
		}
		return HASH
	case '[':
		return LBRACK
	default: // '/'
		if p.readOnly('/') {
			return DQUO
		}
		return QUO
	}
}

func (p *parser) doArithmToken(b byte) Token {
	switch b {
	case '!':
		if p.readOnly('=') {
			return NEQ
		}
		return NOT
	case '=':
		if p.readOnly('=') {
			return EQL
		}
		return ASSIGN
	case '(':
		return LPAREN
	case ')':
		return RPAREN
	case '&':
		if p.readOnly('&') {
			return LAND
		}
		if p.readOnly('=') {
			return ANDASSGN
		}
		return AND
	case '|':
		if p.readOnly('|') {
			return LOR
		}
		if p.readOnly('=') {
			return ORASSGN
		}
		return OR
	case '<':
		switch {
		case p.readOnly('<'):
			if p.readOnly('=') {
				return SHLASSGN
			}
			return SHL
		case p.readOnly('='):
			return LEQ
		}
		return LSS
	case '>':
		switch {
		case p.readOnly('>'):
			if p.readOnly('=') {
				return SHRASSGN
			}
			return SHR
		case p.readOnly('='):
			return GEQ
		}
		return GTR
	case '+':
		if p.readOnly('+') {
			return INC
		}
		if p.readOnly('=') {
			return ADDASSGN
		}
		return ADD
	case '-':
		if p.readOnly('-') {
			return DEC
		}
		if p.readOnly('=') {
			return SUBASSGN
		}
		return SUB
	case '%':
		if p.readOnly('=') {
			return REMASSGN
		}
		return REM
	case '*':
		if p.readOnly('*') {
			return POW
		}
		if p.readOnly('=') {
			return MULASSGN
		}
		return MUL
	case '/':
		if p.readOnly('=') {
			return QUOASSGN
		}
		return QUO
	case '^':
		if p.readOnly('=') {
			return XORASSGN
		}
		return XOR
	case ',':
		return COMMA
	case '?':
		return QUEST
	default: // ':'
		return COLON
	}
}
<|endoftext|>


<commit_before>//go:build tools
// +build tools

package main

import (
	_ "github.com/onsi/ginkgo/v2/ginkgo"
)
<commit_msg>Use tools package for tools.go<commit_after>//go:build tools
// +build tools

package tools

import (
	_ "github.com/onsi/ginkgo/v2/ginkgo"
)
<|endoftext|>


<commit_before>package garbage4

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"os"
	"path"
	"strconv"
	"sync"
	"syscall"
	"unsafe"
)

var (
	encoder           = binary.LittleEndian
	blank             = struct{}{}
	ChannelNameLenErr = fmt.Errorf("channel name cannot exceed %d characters", MAX_CHANNEL_NAME_SIZE)
	ChannelExistsErr  = fmt.Errorf("channel already exists")
	ChannelCreateErr  = fmt.Errorf("channel count not be created")
	pageSize          = os.Getpagesize()
)

type addChannelWork struct {
	err     error
	name    string
	channel *Channel
	c       chan *addChannelWork
}

type Topic struct {
	path         string
	state        *State
	states       *States
	channelsLock sync.RWMutex
	channels     map[string]*Channel
	segment      *Segment
	segmentsLock sync.RWMutex
	segments     map[uint64]*Segment
	addChannel   chan *addChannelWork
	messageAdded chan struct{}
	segmentDone  chan uint64
	dataLock     sync.RWMutex
}

func OpenTopic(name string, config *Configuration) (*Topic, error) {
	t := &Topic{
		path:         path.Join(config.path, name),
		channels:     make(map[string]*Channel),
		segments:     make(map[uint64]*Segment),
		addChannel:   make(chan *addChannelWork),
		segmentDone:  make(chan uint64, 8),
		messageAdded: make(chan struct{}, 64),
	}
	err := loadStates(t)
	if err != nil {
		return nil, err
	}

	if id := t.state.segmentId; id == 0 {
		if err := t.expand(); err != nil {
			return nil, err
		}
		if err := t.states.syncTopic(); err != nil {
			return nil, err
		}
	} else {
		t.segment = openSegment(t, id, false)
		t.segments[id] = t.segment
	}
	go t.worker()
	return t, nil
}

func (t *Topic) Write(data []byte) error {
	length := len(data)

	t.dataLock.Lock()
	start := int(t.state.offset)
	dataStart := start + 4
	dataEnd := dataStart + length

	// do we have enough space in the current segment?
	if dataEnd > MAX_SEGMENT_SIZE {
		if err := t.expand(); err != nil {
			return err
		}
		start = int(SEGMENT_HEADER_SIZE)
		dataStart = start + 4
		dataEnd = dataStart + length
	}

	// write the message length
	encoder.PutUint32(t.segment.data[start:], uint32(length))
	// write the message
	copy(t.segment.data[dataStart:], data)
	// location of the next write
	t.state.offset = uint32(dataEnd)
	// update the size of this segment
	t.segment.size += uint32(length + 4)
	t.dataLock.Unlock()

	// sync the part of the data file we just wrote
	from := start / pageSize * pageSize
	to := dataStart + length - from
	_, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&t.segment.data[from])), uintptr(to), syscall.MS_SYNC)
	if errno != 0 {
		return syscall.Errno(errno)
	}

	// notify channels that a new message is waiting
	t.messageAdded <- blank

	return nil
}

func (t *Topic) Channel(name string) (*Channel, error) {
	if len(name) > MAX_CHANNEL_NAME_SIZE {
		return nil, ChannelNameLenErr
	}
	res := &addChannelWork{
		name: name,
		c:    make(chan *addChannelWork),
	}
	t.addChannel <- res
	<-res.c
	return res.channel, res.err
}

func (t *Topic) expand() error {
	segment := newSegment(t)
	t.segmentsLock.Lock()
	t.segments[segment.id] = segment
	t.segmentsLock.Unlock()
	if t.segment != nil {
		// create a pointer to the next segment id
		t.segment.nextId = segment.id
		if err := t.segment.syncHeader(); err != nil {
			return err
		}
		// it's possible this segment can be cleaned up (if we have no channels)
		t.segmentDone <- t.segment.id
	}
	t.segment = segment
	t.state.offset = SEGMENT_HEADER_SIZE
	t.state.segmentId = segment.id
	return nil
}

func (t *Topic) worker() {
	for {
		select {
		case work := <-t.addChannel:
			work.channel, work.err = t.createChannel(work.name)
			work.c <- work
		case <-t.messageAdded:
			t.channelsLock.RLock()
			for _, c := range t.channels {
				c.notify()
			}
			t.channelsLock.RUnlock()
		case id := <-t.segmentDone:
			t.dataLock.RLock()
			// shortcircuit if this is the topic's segment
			if id == t.segment.id {
				t.dataLock.RUnlock()
				break
			}
			usable := t.isSegmentUsable(id)
			t.dataLock.RUnlock()
			if usable == false {
				t.segmentsLock.Lock()
				segment := t.segments[id]
				delete(t.segments, id)
				t.segmentsLock.Unlock()
				if segment != nil {
					segment.delete()
				}
			}
		}
	}
}

func (t *Topic) createChannel(name string) (*Channel, error) {
	temp := false
	if len(name) == 0 {
		name = "tmp." + strconv.Itoa(rand.Int())
		temp = true
	}
	t.channelsLock.Lock()
	_, exists := t.channels[name]
	if exists {
		t.channelsLock.Unlock()
		if temp {
			// try again until we've created one
			return t.createChannel("")
		}
		return nil, ChannelExistsErr
	}
	defer t.channelsLock.Unlock()

	c := newChannel(t, name)
	if temp {
		c.state = new(State)
	} else {
		c.state = t.states.getOrCreate(name)
		if c.state == nil {
			return nil, ChannelCreateErr
		}
	}

	// if we have a temp channel, or a new channel, set the position to the
	// writer's current position
	if temp || c.state.segmentId == 0 {
		t.dataLock.RLock()
		c.state.offset = t.state.offset
		c.state.segmentId = t.state.segmentId
		t.dataLock.RUnlock()
	}
	t.channels[name] = c
	return c, nil
}

func (t *Topic) read(channel *Channel) []byte {
	state := channel.state

	// no need to lock the channel here since the state of the channel is only
	// ever changed after this point (either to move to the next segment or message)
	t.dataLock.RLock()
	isCurrentSegment := state.segmentId == t.state.segmentId
	isCurrentOffset := state.offset == t.state.offset
	t.dataLock.RUnlock()

	// are we fully caught up with the topic?
	if isCurrentSegment && isCurrentOffset {
		return nil
	}

	segment := t.loadSegment(state.segmentId)
	// If we aren't on the current segment, we might be at the end of an old
	// segment. We can safely read old segments without locking since the only
	// writer (the topic), is done with it
	if !isCurrentSegment {
		if state.offset >= segment.size {
			previousId := state.segmentId
			segment = t.loadSegment(segment.nextId)
			channel.Lock()
			state.segmentId = segment.id
			state.offset = SEGMENT_HEADER_SIZE
			channel.Unlock()
			t.segmentDone <- previousId
		}
	}

	l := encoder.Uint32(segment.data[state.offset:])
	start := state.offset + 4
	end := start + uint32(l)
	return segment.data[start:end]
}

func (t *Topic) loadSegment(id uint64) *Segment {
	t.segmentsLock.RLock()
	segment := t.segments[id]
	t.segmentsLock.RUnlock()
	if segment != nil {
		return segment
	}

	t.segmentsLock.Lock()
	defer t.segmentsLock.Unlock()
	if segment := t.segments[id]; segment != nil {
		return segment
	}
	segment = openSegment(t, id, false)
	t.segments[id] = segment
	return segment
}

func (t *Topic) isSegmentUsable(id uint64) bool {
	t.channelsLock.RLock()
	defer t.channelsLock.RUnlock()
	for _, channel := range t.channels {
		if channel.isSegmentUsable(id) == true {
			return true
		}
	}
	return false
}
<commit_msg>The state file is just a hint about where we might be. It could have stale infomration. For channels, this is fine, it simply means we might get the same message twice (at-least once delivery). For the topic, this is a problem as we'd risk overwriting messages. Thankfully, the segment files themselves tell us everything we need to know about the topic's state.<commit_after>package garbage4

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"os"
	"path"
	"strconv"
	"sync"
	"syscall"
	"unsafe"
)

var (
	encoder           = binary.LittleEndian
	blank             = struct{}{}
	ChannelNameLenErr = fmt.Errorf("channel name cannot exceed %d characters", MAX_CHANNEL_NAME_SIZE)
	ChannelExistsErr  = fmt.Errorf("channel already exists")
	ChannelCreateErr  = fmt.Errorf("channel count not be created")
	pageSize          = os.Getpagesize()
)

type addChannelWork struct {
	err     error
	name    string
	channel *Channel
	c       chan *addChannelWork
}

type Topic struct {
	path         string
	state        *State
	states       *States
	channelsLock sync.RWMutex
	channels     map[string]*Channel
	segment      *Segment
	segmentsLock sync.RWMutex
	segments     map[uint64]*Segment
	addChannel   chan *addChannelWork
	messageAdded chan struct{}
	segmentDone  chan uint64
	dataLock     sync.RWMutex
}

func OpenTopic(name string, config *Configuration) (*Topic, error) {
	t := &Topic{
		path:         path.Join(config.path, name),
		channels:     make(map[string]*Channel),
		segments:     make(map[uint64]*Segment),
		addChannel:   make(chan *addChannelWork),
		segmentDone:  make(chan uint64, 8),
		messageAdded: make(chan struct{}, 64),
	}
	err := loadStates(t)
	if err != nil {
		return nil, err
	}

	if id := t.state.segmentId; id == 0 {
		if err := t.expand(); err != nil {
			return nil, err
		}
		if err := t.states.syncTopic(); err != nil {
			return nil, err
		}
	} else {
		t.findWritePosition(openSegment(t, id, false))
		t.segments[id] = t.segment
	}
	go t.worker()
	return t, nil
}

func (t *Topic) Write(data []byte) error {
	length := len(data)

	t.dataLock.Lock()
	start := int(t.state.offset)
	dataStart := start + 4
	dataEnd := dataStart + length

	// do we have enough space in the current segment?
	if dataEnd > MAX_SEGMENT_SIZE {
		if err := t.expand(); err != nil {
			return err
		}
		start = int(SEGMENT_HEADER_SIZE)
		dataStart = start + 4
		dataEnd = dataStart + length
	}

	// write the message length
	encoder.PutUint32(t.segment.data[start:], uint32(length))
	// write the message
	copy(t.segment.data[dataStart:], data)
	// location of the next write
	t.state.offset = uint32(dataEnd)
	// update the size of this segment
	t.segment.size += uint32(length + 4)
	t.dataLock.Unlock()

	// sync the part of the data file we just wrote
	from := start / pageSize * pageSize
	to := dataStart + length - from
	_, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&t.segment.data[from])), uintptr(to), syscall.MS_SYNC)
	if errno != 0 {
		return syscall.Errno(errno)
	}

	// notify channels that a new message is waiting
	t.messageAdded <- blank

	return nil
}

func (t *Topic) Channel(name string) (*Channel, error) {
	if len(name) > MAX_CHANNEL_NAME_SIZE {
		return nil, ChannelNameLenErr
	}
	res := &addChannelWork{
		name: name,
		c:    make(chan *addChannelWork),
	}
	t.addChannel <- res
	<-res.c
	return res.channel, res.err
}

func (t *Topic) expand() error {
	segment := newSegment(t)
	t.segmentsLock.Lock()
	t.segments[segment.id] = segment
	t.segmentsLock.Unlock()
	if t.segment != nil {
		// create a pointer to the next segment id
		t.segment.nextId = segment.id
		if err := t.segment.syncHeader(); err != nil {
			return err
		}
		// it's possible this segment can be cleaned up (if we have no channels)
		t.segmentDone <- t.segment.id
	}
	t.segment = segment
	t.state.offset = SEGMENT_HEADER_SIZE
	t.state.segmentId = segment.id
	return nil
}

func (t *Topic) worker() {
	for {
		select {
		case work := <-t.addChannel:
			work.channel, work.err = t.createChannel(work.name)
			work.c <- work
		case <-t.messageAdded:
			t.channelsLock.RLock()
			for _, c := range t.channels {
				c.notify()
			}
			t.channelsLock.RUnlock()
		case id := <-t.segmentDone:
			t.dataLock.RLock()
			// shortcircuit if this is the topic's segment
			if id == t.segment.id {
				t.dataLock.RUnlock()
				break
			}
			usable := t.isSegmentUsable(id)
			t.dataLock.RUnlock()
			if usable == false {
				t.segmentsLock.Lock()
				segment := t.segments[id]
				delete(t.segments, id)
				t.segmentsLock.Unlock()
				if segment != nil {
					segment.delete()
				}
			}
		}
	}
}

func (t *Topic) createChannel(name string) (*Channel, error) {
	temp := false
	if len(name) == 0 {
		name = "tmp." + strconv.Itoa(rand.Int())
		temp = true
	}
	t.channelsLock.Lock()
	_, exists := t.channels[name]
	if exists {
		t.channelsLock.Unlock()
		if temp {
			// try again until we've created one
			return t.createChannel("")
		}
		return nil, ChannelExistsErr
	}
	defer t.channelsLock.Unlock()

	c := newChannel(t, name)
	if temp {
		c.state = new(State)
	} else {
		c.state = t.states.getOrCreate(name)
		if c.state == nil {
			return nil, ChannelCreateErr
		}
	}

	// if we have a temp channel, or a new channel, set the position to the
	// writer's current position
	if temp || c.state.segmentId == 0 {
		t.dataLock.RLock()
		c.state.offset = t.state.offset
		c.state.segmentId = t.state.segmentId
		t.dataLock.RUnlock()
	}
	t.channels[name] = c
	return c, nil
}

func (t *Topic) read(channel *Channel) []byte {
	state := channel.state

	// no need to lock the channel here since the state of the channel is only
	// ever changed after this point (either to move to the next segment or message)
	t.dataLock.RLock()
	isCurrentSegment := state.segmentId == t.state.segmentId
	isCurrentOffset := state.offset == t.state.offset
	t.dataLock.RUnlock()

	// are we fully caught up with the topic?
	if isCurrentSegment && isCurrentOffset {
		return nil
	}

	segment := t.loadSegment(state.segmentId)
	// If we aren't on the current segment, we might be at the end of an old
	// segment. We can safely read old segments without locking since the only
	// writer (the topic), is done with it
	if !isCurrentSegment {
		if state.offset >= segment.size {
			previousId := state.segmentId
			segment = t.loadSegment(segment.nextId)
			channel.Lock()
			state.segmentId = segment.id
			state.offset = SEGMENT_HEADER_SIZE
			channel.Unlock()
			t.segmentDone <- previousId
		}
	}

	l := encoder.Uint32(segment.data[state.offset:])
	start := state.offset + 4
	end := start + uint32(l)
	return segment.data[start:end]
}

func (t *Topic) loadSegment(id uint64) *Segment {
	t.segmentsLock.RLock()
	segment := t.segments[id]
	t.segmentsLock.RUnlock()
	if segment != nil {
		return segment
	}

	t.segmentsLock.Lock()
	defer t.segmentsLock.Unlock()
	if segment := t.segments[id]; segment != nil {
		return segment
	}
	segment = openSegment(t, id, false)
	t.segments[id] = segment
	return segment
}

func (t *Topic) isSegmentUsable(id uint64) bool {
	t.channelsLock.RLock()
	defer t.channelsLock.RUnlock()
	for _, channel := range t.channels {
		if channel.isSegmentUsable(id) == true {
			return true
		}
	}
	return false
}

// The topic's persisted state could be behind from the actual state. However,
// we can determine the actual state based on the data which s guaranteed to be
// up to date. Namely a segment's nextId and its size (note, the persisted size
// also isn't necessarily correct, but openSegment fixes this)
func (t *Topic) findWritePosition(segment *Segment) {
	for segment.nextId != 0 {
		segment = openSegment(t, segment.nextId, false)
	}
	t.segment = segment
	t.state.segmentId = segment.id
	t.state.offset = segment.size
}
<|endoftext|>


<commit_before>package trace

import (
	"github.com/gin-gonic/gin"
	"golang.org/x/net/trace"
)

// Trace the request and adds some helper methods to subsequent handlers
func Trace() gin.HandlerFunc {
	return func(c *gin.Context) {

		// a new trace
		tr := trace.New(c.HandlerName(), c.Request.URL.Path)

		c.Next()

		// finish the trace
		tr.Finish()

	}
}

// r.GET("/debug/requests", trace.RequestsController)
func RequestsController(c *gin.Context) {
	// render the requests page
	trace.Render(c.Writer, c.Request, false)

	return
}

func EventsController(c *gin.Context) {
	// render the events page
	trace.RenderEvents(c.Writer, c.Request, false)

	return
}
<commit_msg>remove events handler and clean up comments<commit_after>package trace

import (
	"github.com/gin-gonic/gin"
	"golang.org/x/net/trace"
)

// Trace will gather information from the request and also add the trace methods to handlers
func Trace() gin.HandlerFunc {
	return func(c *gin.Context) {

		// a new trace
		tr := trace.New(c.HandlerName(), c.Request.URL.Path)

		// access the trace methods from subsequent handlers
		c.Set("trace", tr)

		c.Next()

		// finish the trace
		tr.Finish()

	}
}

// TraceController returns the default trace requests page
// example handler: r.GET("/debug/requests", trace.RequestsController)
func TraceController(c *gin.Context) {
	// render the requests page
	trace.Render(c.Writer, c.Request, false)
	return
}
<|endoftext|>


<commit_before>package nvd

import (
	"bufio"
	"compress/gzip"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/coreos/clair/database"
	"github.com/coreos/clair/updater"
	cerrors "github.com/coreos/clair/utils/errors"
	"github.com/coreos/pkg/capnslog"
)

const (
	dataFeedURL     string = "http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%s.xml.gz"
	dataFeedMetaURL string = "http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%s.meta"

	metadataKey string = "NVD"
)

var (
	log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/metadata_fetchers")
)

type NVDMetadataFetcher struct {
	localPath      string
	dataFeedHashes map[string]string
	lock           sync.Mutex

	metadata map[string]NVDMetadata
}

type NVDMetadata struct {
	CVSSv2 NVDmetadataCVSSv2
}

type NVDmetadataCVSSv2 struct {
	Vectors string
	Score   float64
}

func init() {
	updater.RegisterMetadataFetcher("NVD", &NVDMetadataFetcher{})
}

func (fetcher *NVDMetadataFetcher) Load(datastore database.Datastore) error {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	var err error
	fetcher.metadata = make(map[string]NVDMetadata)

	// Init if necessary.
	if fetcher.localPath == "" {
		// Create a temporary folder to store the NVD data and create hashes struct.
		if fetcher.localPath, err = ioutil.TempDir(os.TempDir(), "nvd-data"); err != nil {
			return cerrors.ErrFilesystem
		}

		fetcher.dataFeedHashes = make(map[string]string)
	}

	// Get data feeds.
	dataFeedReaders, dataFeedHashes, err := getDataFeeds(fetcher.dataFeedHashes, fetcher.localPath)
	if err != nil {
		return err
	}
	fetcher.dataFeedHashes = dataFeedHashes

	// Parse data feeds.
	for dataFeedName, dataFeedReader := range dataFeedReaders {
		var nvd nvd
		if err = xml.NewDecoder(dataFeedReader).Decode(&nvd); err != nil {
			log.Errorf("could not decode NVD data feed '%s': %s", dataFeedName, err)
			return cerrors.ErrCouldNotParse
		}

		// For each entry of this data feed:
		for _, nvdEntry := range nvd.Entries {
			// Create metadata entry.
			if metadata := nvdEntry.Metadata(); metadata != nil {
				fetcher.metadata[nvdEntry.Name] = *metadata
			}
		}

		dataFeedReader.Close()
	}

	return nil
}

func (fetcher *NVDMetadataFetcher) AddMetadata(vulnerability *updater.VulnerabilityWithLock) error {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	if nvdMetadata, ok := fetcher.metadata[vulnerability.Name]; ok {
		vulnerability.Lock.Lock()

		// Create Metadata map if necessary.
		if vulnerability.Metadata == nil {
			vulnerability.Metadata = make(map[string]interface{})
		}

		vulnerability.Metadata[metadataKey] = nvdMetadata

		vulnerability.Lock.Unlock()
	}

	return nil
}

func (fetcher *NVDMetadataFetcher) Unload() {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	fetcher.metadata = nil
}

func (fetcher *NVDMetadataFetcher) Clean() {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	os.RemoveAll(fetcher.localPath)
}

func getDataFeeds(dataFeedHashes map[string]string, localPath string) (map[string]NestedReadCloser, map[string]string, error) {
	var dataFeedNames []string
	for y := 2002; y <= time.Now().Year(); y++ {
		dataFeedNames = append(dataFeedNames, strconv.Itoa(y))
	}

	// Get hashes for these feeds.
	for _, dataFeedName := range dataFeedNames {
		hash, err := getHashFromMetaURL(fmt.Sprintf(dataFeedMetaURL, dataFeedName))
		if err != nil {
			log.Warningf("could get get NVD data feed hash '%s': %s", dataFeedName, err)

			// It's not a big deal, no need interrupt, we're just going to download it again then.
			continue
		}

		dataFeedHashes[dataFeedName] = hash
	}

	// Create io.Reader for every data feed.
	dataFeedReaders := make(map[string]NestedReadCloser)
	for _, dataFeedName := range dataFeedNames {
		fileName := localPath + dataFeedName + ".xml"

		if h, ok := dataFeedHashes[dataFeedName]; ok && h == dataFeedHashes[dataFeedName] {
			// The hash is known, the disk should contains the feed. Try to read from it.
			if localPath != "" {
				if f, err := os.Open(fileName); err == nil {
					dataFeedReaders[dataFeedName] = NestedReadCloser{
						Reader:            f,
						NestedReadClosers: []io.ReadCloser{f},
					}
					continue
				}
			}

			// Download data feed.
			r, err := http.Get(fmt.Sprintf(dataFeedURL, dataFeedName))
			if err != nil {
				log.Errorf("could not download NVD data feed file '%s': %s", dataFeedName, err)
				return dataFeedReaders, dataFeedHashes, cerrors.ErrCouldNotDownload
			}

			// Un-gzip it.
			gr, err := gzip.NewReader(r.Body)
			if err != nil {
				log.Errorf("could not read NVD data feed file '%s': %s", dataFeedName, err)
				return dataFeedReaders, dataFeedHashes, cerrors.ErrCouldNotDownload
			}

			// Store it to a file at the same time if possible.
			if f, err := os.Create(fileName); err == nil {
				nrc := NestedReadCloser{
					Reader:            io.TeeReader(gr, f),
					NestedReadClosers: []io.ReadCloser{r.Body, gr, f},
				}
				dataFeedReaders[dataFeedName] = nrc
			} else {
				nrc := NestedReadCloser{
					Reader:            gr,
					NestedReadClosers: []io.ReadCloser{gr, r.Body},
				}
				dataFeedReaders[dataFeedName] = nrc

				log.Warningf("could not store NVD data feed to filesystem: %s", err)
			}
		}
	}

	return dataFeedReaders, dataFeedHashes, nil
}

func getHashFromMetaURL(metaURL string) (string, error) {
	r, err := http.Get(metaURL)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()

	scanner := bufio.NewScanner(r.Body)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "sha256:") {
			return strings.TrimPrefix(line, "sha256:"), nil
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}

	return "", errors.New("invalid .meta file format")
}
<commit_msg>updater: Set vulns' Severity from NVD metadata fetcher if unknown<commit_after>package nvd

import (
	"bufio"
	"compress/gzip"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/coreos/clair/database"
	"github.com/coreos/clair/updater"
	cerrors "github.com/coreos/clair/utils/errors"
	"github.com/coreos/clair/utils/types"
	"github.com/coreos/pkg/capnslog"
)

const (
	dataFeedURL     string = "http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%s.xml.gz"
	dataFeedMetaURL string = "http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%s.meta"

	metadataKey string = "NVD"
)

var (
	log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/metadata_fetchers")
)

type NVDMetadataFetcher struct {
	localPath      string
	dataFeedHashes map[string]string
	lock           sync.Mutex

	metadata map[string]NVDMetadata
}

type NVDMetadata struct {
	CVSSv2 NVDmetadataCVSSv2
}

type NVDmetadataCVSSv2 struct {
	Vectors string
	Score   float64
}

func init() {
	updater.RegisterMetadataFetcher("NVD", &NVDMetadataFetcher{})
}

func (fetcher *NVDMetadataFetcher) Load(datastore database.Datastore) error {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	var err error
	fetcher.metadata = make(map[string]NVDMetadata)

	// Init if necessary.
	if fetcher.localPath == "" {
		// Create a temporary folder to store the NVD data and create hashes struct.
		if fetcher.localPath, err = ioutil.TempDir(os.TempDir(), "nvd-data"); err != nil {
			return cerrors.ErrFilesystem
		}

		fetcher.dataFeedHashes = make(map[string]string)
	}

	// Get data feeds.
	dataFeedReaders, dataFeedHashes, err := getDataFeeds(fetcher.dataFeedHashes, fetcher.localPath)
	if err != nil {
		return err
	}
	fetcher.dataFeedHashes = dataFeedHashes

	// Parse data feeds.
	for dataFeedName, dataFeedReader := range dataFeedReaders {
		var nvd nvd
		if err = xml.NewDecoder(dataFeedReader).Decode(&nvd); err != nil {
			log.Errorf("could not decode NVD data feed '%s': %s", dataFeedName, err)
			return cerrors.ErrCouldNotParse
		}

		// For each entry of this data feed:
		for _, nvdEntry := range nvd.Entries {
			// Create metadata entry.
			if metadata := nvdEntry.Metadata(); metadata != nil {
				fetcher.metadata[nvdEntry.Name] = *metadata
			}
		}

		dataFeedReader.Close()
	}

	return nil
}

func (fetcher *NVDMetadataFetcher) AddMetadata(vulnerability *updater.VulnerabilityWithLock) error {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	if nvdMetadata, ok := fetcher.metadata[vulnerability.Name]; ok {
		vulnerability.Lock.Lock()

		// Create Metadata map if necessary and assign the NVD metadata.
		if vulnerability.Metadata == nil {
			vulnerability.Metadata = make(map[string]interface{})
		}
		vulnerability.Metadata[metadataKey] = nvdMetadata

		// Set the Severity using the CVSSv2 Score if none is set yet.
		if vulnerability.Severity == "" || vulnerability.Severity == types.Unknown {
			vulnerability.Severity = scoreToPriority(nvdMetadata.CVSSv2.Score)
		}

		vulnerability.Lock.Unlock()
	}

	return nil
}

func (fetcher *NVDMetadataFetcher) Unload() {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	fetcher.metadata = nil
}

func (fetcher *NVDMetadataFetcher) Clean() {
	fetcher.lock.Lock()
	defer fetcher.lock.Unlock()

	os.RemoveAll(fetcher.localPath)
}

func getDataFeeds(dataFeedHashes map[string]string, localPath string) (map[string]NestedReadCloser, map[string]string, error) {
	var dataFeedNames []string
	for y := 2002; y <= time.Now().Year(); y++ {
		dataFeedNames = append(dataFeedNames, strconv.Itoa(y))
	}

	// Get hashes for these feeds.
	for _, dataFeedName := range dataFeedNames {
		hash, err := getHashFromMetaURL(fmt.Sprintf(dataFeedMetaURL, dataFeedName))
		if err != nil {
			log.Warningf("could get get NVD data feed hash '%s': %s", dataFeedName, err)

			// It's not a big deal, no need interrupt, we're just going to download it again then.
			continue
		}

		dataFeedHashes[dataFeedName] = hash
	}

	// Create io.Reader for every data feed.
	dataFeedReaders := make(map[string]NestedReadCloser)
	for _, dataFeedName := range dataFeedNames {
		fileName := localPath + dataFeedName + ".xml"

		if h, ok := dataFeedHashes[dataFeedName]; ok && h == dataFeedHashes[dataFeedName] {
			// The hash is known, the disk should contains the feed. Try to read from it.
			if localPath != "" {
				if f, err := os.Open(fileName); err == nil {
					dataFeedReaders[dataFeedName] = NestedReadCloser{
						Reader:            f,
						NestedReadClosers: []io.ReadCloser{f},
					}
					continue
				}
			}

			// Download data feed.
			r, err := http.Get(fmt.Sprintf(dataFeedURL, dataFeedName))
			if err != nil {
				log.Errorf("could not download NVD data feed file '%s': %s", dataFeedName, err)
				return dataFeedReaders, dataFeedHashes, cerrors.ErrCouldNotDownload
			}

			// Un-gzip it.
			gr, err := gzip.NewReader(r.Body)
			if err != nil {
				log.Errorf("could not read NVD data feed file '%s': %s", dataFeedName, err)
				return dataFeedReaders, dataFeedHashes, cerrors.ErrCouldNotDownload
			}

			// Store it to a file at the same time if possible.
			if f, err := os.Create(fileName); err == nil {
				nrc := NestedReadCloser{
					Reader:            io.TeeReader(gr, f),
					NestedReadClosers: []io.ReadCloser{r.Body, gr, f},
				}
				dataFeedReaders[dataFeedName] = nrc
			} else {
				nrc := NestedReadCloser{
					Reader:            gr,
					NestedReadClosers: []io.ReadCloser{gr, r.Body},
				}
				dataFeedReaders[dataFeedName] = nrc

				log.Warningf("could not store NVD data feed to filesystem: %s", err)
			}
		}
	}

	return dataFeedReaders, dataFeedHashes, nil
}

func getHashFromMetaURL(metaURL string) (string, error) {
	r, err := http.Get(metaURL)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()

	scanner := bufio.NewScanner(r.Body)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "sha256:") {
			return strings.TrimPrefix(line, "sha256:"), nil
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}

	return "", errors.New("invalid .meta file format")
}

// scoreToPriority converts the CVSS Score (0.0 - 10.0) into user-friendy
// types.Priority following the qualitative rating scale available in the
// CVSS v3.0 specification (https://www.first.org/cvss/specification-document),
// Table 14. The Negligible level is set for CVSS scores between [0, 1),
// replacing the specified None level, originally used for a score of 0.
func scoreToPriority(score float64) types.Priority {
	switch {
	case score < 1.0:
		return types.Negligible
	case score < 3.9:
		return types.Low
	case score < 6.9:
		return types.Medium
	case score < 8.9:
		return types.High
	case score <= 10:
		return types.Critical
	}
	return types.Unknown
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package testing

import (
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"

	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)

type FakeImageService struct {
	sync.Mutex

	FakeImageSize uint64
	Called        []string
	Images        map[string]*runtimeapi.Image

	pulledImages []*pulledImage

	FakeFilesystemUsage []*runtimeapi.FilesystemUsage
}

func (r *FakeImageService) SetFakeImages(images []string) {
	r.Lock()
	defer r.Unlock()

	r.Images = make(map[string]*runtimeapi.Image)
	for _, image := range images {
		r.Images[image] = r.makeFakeImage(image)
	}
}

func (r *FakeImageService) SetFakeImageSize(size uint64) {
	r.Lock()
	defer r.Unlock()

	r.FakeImageSize = size
}

func (r *FakeImageService) SetFakeFilesystemUsage(usage []*runtimeapi.FilesystemUsage) {
	r.Lock()
	defer r.Unlock()

	r.FakeFilesystemUsage = usage
}

func NewFakeImageService() *FakeImageService {
	return &FakeImageService{
		Called: make([]string, 0),
		Images: make(map[string]*runtimeapi.Image),
	}
}

func (r *FakeImageService) makeFakeImage(image string) *runtimeapi.Image {
	return &runtimeapi.Image{
		Id:       image,
		Size_:    r.FakeImageSize,
		RepoTags: []string{image},
	}
}

// stringInSlice returns true if s is in list
func stringInSlice(s string, list []string) bool {
	for _, v := range list {
		if v == s {
			return true
		}
	}

	return false
}

func (r *FakeImageService) ListImages(filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "ListImages")

	images := make([]*runtimeapi.Image, 0)
	for _, img := range r.Images {
		if filter != nil && filter.Image != nil {
			if !stringInSlice(filter.Image.Image, img.RepoTags) {
				continue
			}
		}

		images = append(images, img)
	}
	return images, nil
}

func (r *FakeImageService) ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "ImageStatus")

	return r.Images[image.Image], nil
}

func (r *FakeImageService) PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "PullImage")

	r.pulledImages = append(r.pulledImages, &pulledImage{imageSpec: image, authConfig: auth})
	// ImageID should be randomized for real container runtime, but here just use
	// image's name for easily making fake images.
	imageID := image.Image
	if _, ok := r.Images[imageID]; !ok {
		r.Images[imageID] = r.makeFakeImage(image.Image)
	}

	return imageID, nil
}

func (r *FakeImageService) RemoveImage(image *runtimeapi.ImageSpec) error {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "RemoveImage")

	// Remove the image
	delete(r.Images, image.Image)

	return nil
}

// ImageFsInfo returns information of the filesystem that is used to store images.
func (r *FakeImageService) ImageFsInfo() ([]*runtimeapi.FilesystemUsage, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "ImageFsInfo")

	return r.FakeFilesystemUsage, nil
}

func (r *FakeImageService) AssertImagePulledWithAuth(t *testing.T, image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, failMsg string) {
	r.Lock()
	defer r.Unlock()
	expected := &pulledImage{imageSpec: image, authConfig: auth}
	assert.Contains(t, r.pulledImages, expected, failMsg)
}

type pulledImage struct {
	imageSpec  *runtimeapi.ImageSpec
	authConfig *runtimeapi.AuthConfig
}
<commit_msg>Support injecting errors for `FakeImageService`<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package testing

import (
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"

	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)

type FakeImageService struct {
	sync.Mutex

	FakeImageSize uint64
	Called        []string
	Errors        map[string][]error
	Images        map[string]*runtimeapi.Image

	pulledImages []*pulledImage

	FakeFilesystemUsage []*runtimeapi.FilesystemUsage
}

func (r *FakeImageService) SetFakeImages(images []string) {
	r.Lock()
	defer r.Unlock()

	r.Images = make(map[string]*runtimeapi.Image)
	for _, image := range images {
		r.Images[image] = r.makeFakeImage(image)
	}
}

func (r *FakeImageService) SetFakeImageSize(size uint64) {
	r.Lock()
	defer r.Unlock()

	r.FakeImageSize = size
}

func (r *FakeImageService) SetFakeFilesystemUsage(usage []*runtimeapi.FilesystemUsage) {
	r.Lock()
	defer r.Unlock()

	r.FakeFilesystemUsage = usage
}

func NewFakeImageService() *FakeImageService {
	return &FakeImageService{
		Called: make([]string, 0),
		Images: make(map[string]*runtimeapi.Image),
	}
}

func (r *FakeImageService) makeFakeImage(image string) *runtimeapi.Image {
	return &runtimeapi.Image{
		Id:       image,
		Size_:    r.FakeImageSize,
		RepoTags: []string{image},
	}
}

// stringInSlice returns true if s is in list
func stringInSlice(s string, list []string) bool {
	for _, v := range list {
		if v == s {
			return true
		}
	}

	return false
}

func (r *FakeImageService) InjectError(f string, err error) {
	r.Lock()
	defer r.Unlock()
	r.Errors[f] = append(r.Errors[f], err)
}

// caller of popError must grab a lock.
func (r *FakeImageService) popError(f string) error {
	if r.Errors == nil {
		return nil
	}
	errs := r.Errors[f]
	if len(errs) == 0 {
		return nil
	}
	err, errs := errs[0], errs[1:]
	r.Errors[f] = errs
	return err
}

func (r *FakeImageService) ListImages(filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "ListImages")
	if err := r.popError("ListImages"); err != nil {
		return nil, err
	}

	images := make([]*runtimeapi.Image, 0)
	for _, img := range r.Images {
		if filter != nil && filter.Image != nil {
			if !stringInSlice(filter.Image.Image, img.RepoTags) {
				continue
			}
		}

		images = append(images, img)
	}
	return images, nil
}

func (r *FakeImageService) ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "ImageStatus")
	if err := r.popError("ImageStatus"); err != nil {
		return nil, err
	}

	return r.Images[image.Image], nil
}

func (r *FakeImageService) PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "PullImage")
	if err := r.popError("PullImage"); err != nil {
		return "", err
	}

	r.pulledImages = append(r.pulledImages, &pulledImage{imageSpec: image, authConfig: auth})
	// ImageID should be randomized for real container runtime, but here just use
	// image's name for easily making fake images.
	imageID := image.Image
	if _, ok := r.Images[imageID]; !ok {
		r.Images[imageID] = r.makeFakeImage(image.Image)
	}

	return imageID, nil
}

func (r *FakeImageService) RemoveImage(image *runtimeapi.ImageSpec) error {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "RemoveImage")
	if err := r.popError("RemoveImage"); err != nil {
		return err
	}

	// Remove the image
	delete(r.Images, image.Image)

	return nil
}

// ImageFsInfo returns information of the filesystem that is used to store images.
func (r *FakeImageService) ImageFsInfo() ([]*runtimeapi.FilesystemUsage, error) {
	r.Lock()
	defer r.Unlock()

	r.Called = append(r.Called, "ImageFsInfo")
	if err := r.popError("ImageFsInfo"); err != nil {
		return nil, err
	}

	return r.FakeFilesystemUsage, nil
}

func (r *FakeImageService) AssertImagePulledWithAuth(t *testing.T, image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, failMsg string) {
	r.Lock()
	defer r.Unlock()
	expected := &pulledImage{imageSpec: image, authConfig: auth}
	assert.Contains(t, r.pulledImages, expected, failMsg)
}

type pulledImage struct {
	imageSpec  *runtimeapi.ImageSpec
	authConfig *runtimeapi.AuthConfig
}
<|endoftext|>


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

package ts

import (
	"time"

	"github.com/cockroachdb/cockroach/client"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
)

// DB provides Cockroach's Time Series API.
type DB struct {
	db *client.DB
}

// NewDB creates a new DB instance.
func NewDB(db *client.DB) *DB {
	return &DB{
		db: db,
	}
}

// PollSource begins a Goroutine which periodically queries the supplied
// DataSource for time series data, storing the returned data in the server.
// Stored data will be sampled using the provided Resolution. The polling
// process will continue until the provided stop.Stopper is stopped.
func (db *DB) PollSource(source DataSource, frequency time.Duration, r Resolution, stopper *stop.Stopper) {
	p := &poller{
		db:        db,
		source:    source,
		frequency: frequency,
		r:         r,
		stopper:   stopper,
	}
	p.start()
}

// A DataSource can be queryied for a slice of time series data.
type DataSource interface {
	GetTimeSeriesData() []TimeSeriesData
}

// poller maintains information for a polling process started by PollSource().
type poller struct {
	db        *DB
	source    DataSource
	frequency time.Duration
	r         Resolution
	stopper   *stop.Stopper
}

// start begins the goroutine for this poller, which will periodically request
// time series data from the DataSource and store it.
func (p *poller) start() {
	p.stopper.RunWorker(func() {
		ticker := time.NewTicker(p.frequency)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				p.poll()
			case <-p.stopper.ShouldStop():
				return
			}
		}
	})
}

// poll retrieves data from the underlying DataSource a single time, storing any
// returned time series data on the server.
func (p *poller) poll() {
	p.stopper.RunTask(func() {
		data := p.source.GetTimeSeriesData()
		if len(data) == 0 {
			return
		}

		if err := p.db.StoreData(p.r, data); err != nil {
			log.Warningf("error writing time series data: %s", err)
		}
	})
}

// StoreData writes the supplied time series data to the cockroach server.
// Stored data will be sampled at the supplied resolution.
func (db *DB) StoreData(r Resolution, data []TimeSeriesData) error {
	var kvs []roachpb.KeyValue

	// Process data collection: data is converted to internal format, and a key
	// is generated for each internal message.
	for _, d := range data {
		idatas, err := d.ToInternal(r.KeyDuration(), r.SampleDuration())
		if err != nil {
			return err
		}
		for _, idata := range idatas {
			var value roachpb.Value
			if err := value.SetProto(idata); err != nil {
				return err
			}
			kvs = append(kvs, roachpb.KeyValue{
				Key:   MakeDataKey(d.Name, d.Source, r, idata.StartTimestampNanos),
				Value: value,
			})
		}
	}

	// Send the individual internal merge requests.
	// TODO(mrtracy): In the likely event that there are multiple values to
	// merge, they should be batched together instead of being called
	// individually. However, BatchRequest currently does not support
	// MergeRequest, probably because it cannot be part of a
	// transaction. Look into batching this.
	for _, kv := range kvs {
		// Note, this looks like a batch, but isn't a batch because we only add a
		// single request to it.
		b := &client.Batch{}
		b.InternalAddRequest(&roachpb.MergeRequest{
			Span: roachpb.Span{
				Key: kv.Key,
			},
			Value: kv.Value,
		})
		if err := db.db.Run(b); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>ts: batch Merge requests<commit_after>// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Matt Tracy (matt@cockroachlabs.com)

package ts

import (
	"time"

	"github.com/cockroachdb/cockroach/client"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
)

// DB provides Cockroach's Time Series API.
type DB struct {
	db *client.DB
}

// NewDB creates a new DB instance.
func NewDB(db *client.DB) *DB {
	return &DB{
		db: db,
	}
}

// PollSource begins a Goroutine which periodically queries the supplied
// DataSource for time series data, storing the returned data in the server.
// Stored data will be sampled using the provided Resolution. The polling
// process will continue until the provided stop.Stopper is stopped.
func (db *DB) PollSource(source DataSource, frequency time.Duration, r Resolution, stopper *stop.Stopper) {
	p := &poller{
		db:        db,
		source:    source,
		frequency: frequency,
		r:         r,
		stopper:   stopper,
	}
	p.start()
}

// A DataSource can be queryied for a slice of time series data.
type DataSource interface {
	GetTimeSeriesData() []TimeSeriesData
}

// poller maintains information for a polling process started by PollSource().
type poller struct {
	db        *DB
	source    DataSource
	frequency time.Duration
	r         Resolution
	stopper   *stop.Stopper
}

// start begins the goroutine for this poller, which will periodically request
// time series data from the DataSource and store it.
func (p *poller) start() {
	p.stopper.RunWorker(func() {
		ticker := time.NewTicker(p.frequency)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				p.poll()
			case <-p.stopper.ShouldStop():
				return
			}
		}
	})
}

// poll retrieves data from the underlying DataSource a single time, storing any
// returned time series data on the server.
func (p *poller) poll() {
	p.stopper.RunTask(func() {
		data := p.source.GetTimeSeriesData()
		if len(data) == 0 {
			return
		}

		if err := p.db.StoreData(p.r, data); err != nil {
			log.Warningf("error writing time series data: %s", err)
		}
	})
}

// StoreData writes the supplied time series data to the cockroach server.
// Stored data will be sampled at the supplied resolution.
func (db *DB) StoreData(r Resolution, data []TimeSeriesData) error {
	var kvs []roachpb.KeyValue

	// Process data collection: data is converted to internal format, and a key
	// is generated for each internal message.
	for _, d := range data {
		idatas, err := d.ToInternal(r.KeyDuration(), r.SampleDuration())
		if err != nil {
			return err
		}
		for _, idata := range idatas {
			var value roachpb.Value
			if err := value.SetProto(idata); err != nil {
				return err
			}
			kvs = append(kvs, roachpb.KeyValue{
				Key:   MakeDataKey(d.Name, d.Source, r, idata.StartTimestampNanos),
				Value: value,
			})
		}
	}

	// Send the individual internal merge requests.
	b := client.Batch{}
	for _, kv := range kvs {
		b.InternalAddRequest(&roachpb.MergeRequest{
			Span: roachpb.Span{
				Key: kv.Key,
			},
			Value: kv.Value,
		})
	}

	return db.db.Run(&b)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

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

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

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

// route.go provides methods to perform actions on the route resource.

package test

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"sync"
	"sync/atomic"

	"golang.org/x/sync/errgroup"
	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/logging"
	"knative.dev/pkg/test/spoof"
)

// Prober is the interface for a prober, which checks the result of the probes when stopped.
type Prober interface {
	// SLI returns the "service level indicator" for the prober, which is the observed
	// success rate of the probes.  This will panic if the prober has not been stopped.
	SLI() (total int64, failures int64)

	// Stop terminates the prober, returning any observed errors.
	// Implementations may choose to put additional requirements on
	// the prober, which may cause this to block (e.g. a minimum number
	// of probes to achieve a population suitable for SLI measurement).
	Stop() error
}

type prober struct {
	// These shouldn't change after creation
	logf          logging.FormatLogger
	url           *url.URL
	minimumProbes int64

	requests int64
	failures int64

	// This channel is simply closed when minimumProbes has been satisfied.
	minDoneCh chan struct{}

	errGrp *errgroup.Group
	ctx    context.Context
	cancel context.CancelFunc
}

// prober implements Prober
var _ Prober = (*prober)(nil)

// SLI implements Prober
func (p *prober) SLI() (int64, int64) {
	return atomic.LoadInt64(&p.requests), atomic.LoadInt64(&p.failures)
}

// Stop implements Prober
func (p *prober) Stop() error {
	// Wait for either an error to happen or the minimumProbes we want.
	select {
	case <-p.ctx.Done():
	case <-p.minDoneCh:
	}

	// Stop all probing.
	p.cancel()

	return p.errGrp.Wait()
}

// ProberManager is the interface for spawning probers, and checking their results.
type ProberManager interface {
	// The ProberManager should expose a way to collectively reason about spawned
	// probes as a sort of aggregating Prober.
	Prober

	// Spawn creates a new Prober
	Spawn(url *url.URL) Prober

	// Foreach iterates over the probers spawned by this ProberManager.
	Foreach(func(url *url.URL, p Prober))
}

type manager struct {
	// Should not change after creation
	logf      logging.FormatLogger
	clients   *Clients
	minProbes int64

	m                sync.RWMutex
	probes           map[*url.URL]Prober
	transportOptions []spoof.TransportOption
}

var _ ProberManager = (*manager)(nil)

// Spawn implements ProberManager
func (m *manager) Spawn(url *url.URL) Prober {
	m.m.Lock()
	defer m.m.Unlock()

	if p, ok := m.probes[url]; ok {
		return p
	}

	m.logf("Starting Route prober for %s.", url)

	ctx, cancel := context.WithCancel(context.Background())
	errGrp, ctx := errgroup.WithContext(ctx)

	p := &prober{
		logf:          m.logf,
		url:           url,
		minimumProbes: m.minProbes,

		minDoneCh: make(chan struct{}),

		errGrp: errGrp,
		ctx:    ctx,
		cancel: cancel,
	}
	m.probes[url] = p

	errGrp.Go(func() error {
		client, err := pkgTest.NewSpoofingClient(m.clients.KubeClient, m.logf, url.Hostname(), ServingFlags.ResolvableDomain, m.transportOptions...)
		if err != nil {
			return fmt.Errorf("failed to generate client: %w", err)
		}

		req, err := http.NewRequest(http.MethodGet, url.String(), nil)
		if err != nil {
			return fmt.Errorf("failed to generate request: %w", err)
		}

		// We keep polling the domain and accumulate success rates
		// to ultimately establish the SLI and compare to the SLO.
		for {
			select {
			case <-ctx.Done():
				return nil
			default:
				res, err := client.Do(req)
				if atomic.AddInt64(&p.requests, 1) == p.minimumProbes {
					close(p.minDoneCh)
				}
				if err != nil {
					atomic.AddInt64(&p.failures, 1)
				} else if res.StatusCode != http.StatusOK {
					p.logf("%q status = %d, want: %d", p.url, res.StatusCode, http.StatusOK)
					p.logf("response: %s", res)
					atomic.AddInt64(&p.failures, 1)
				}
			}
		}
	})
	return p
}

// Stop implements ProberManager
func (m *manager) Stop() error {
	m.m.Lock()
	defer m.m.Unlock()

	m.logf("Stopping all probers")

	errgrp := &errgroup.Group{}
	for _, prober := range m.probes {
		errgrp.Go(prober.Stop)
	}
	return errgrp.Wait()
}

// SLI implements Prober
func (m *manager) SLI() (total int64, failures int64) {
	m.m.RLock()
	defer m.m.RUnlock()
	for _, prober := range m.probes {
		pt, pf := prober.SLI()
		total += pt
		failures += pf
	}
	return
}

// Foreach implements ProberManager
func (m *manager) Foreach(f func(url *url.URL, p Prober)) {
	m.m.RLock()
	defer m.m.RUnlock()

	for url, prober := range m.probes {
		f(url, prober)
	}
}

// NewProberManager creates a new manager for probes.
func NewProberManager(logf logging.FormatLogger, clients *Clients, minProbes int64, opts ...spoof.TransportOption) ProberManager {
	return &manager{
		logf:             logf,
		clients:          clients,
		minProbes:        minProbes,
		probes:           make(map[*url.URL]Prober),
		transportOptions: opts,
	}
}

// RunRouteProber starts a single Prober of the given domain.
func RunRouteProber(logf logging.FormatLogger, clients *Clients, url *url.URL, opts ...spoof.TransportOption) Prober {
	// Default to 10 probes
	pm := NewProberManager(logf, clients, 10, opts...)
	pm.Spawn(url)
	return pm
}

// AssertProberDefault is a helper for stopping the Prober and checking its SLI
// against the default SLO, which requires perfect responses.
// This takes `testing.T` so that it may be used in `defer`.
func AssertProberDefault(t pkgTest.T, p Prober) {
	t.Helper()
	if err := p.Stop(); err != nil {
		t.Error("Stop()", "error", err.Error())
	}
	// Default to 100% correct (typically used in conjunction with the low probe count above)
	if err := CheckSLO(1.0, t.Name(), p); err != nil {
		t.Error("CheckSLO()", "error", err.Error())
	}
}

// CheckSLO compares the SLI of the given prober against the SLO, erroring if too low.
func CheckSLO(slo float64, name string, p Prober) error {
	total, failures := p.SLI()

	successRate := float64(total-failures) / float64(total)
	if successRate < slo {
		return fmt.Errorf("SLI for %q = %f, wanted >= %f", name, successRate, slo)
	}
	return nil
}
<commit_msg>Print error when test prober gets error (#8094)<commit_after>/*
Copyright 2019 The Knative Authors

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

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

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

// route.go provides methods to perform actions on the route resource.

package test

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"sync"
	"sync/atomic"

	"golang.org/x/sync/errgroup"
	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/logging"
	"knative.dev/pkg/test/spoof"
)

// Prober is the interface for a prober, which checks the result of the probes when stopped.
type Prober interface {
	// SLI returns the "service level indicator" for the prober, which is the observed
	// success rate of the probes.  This will panic if the prober has not been stopped.
	SLI() (total int64, failures int64)

	// Stop terminates the prober, returning any observed errors.
	// Implementations may choose to put additional requirements on
	// the prober, which may cause this to block (e.g. a minimum number
	// of probes to achieve a population suitable for SLI measurement).
	Stop() error
}

type prober struct {
	// These shouldn't change after creation
	logf          logging.FormatLogger
	url           *url.URL
	minimumProbes int64

	requests int64
	failures int64

	// This channel is simply closed when minimumProbes has been satisfied.
	minDoneCh chan struct{}

	errGrp *errgroup.Group
	ctx    context.Context
	cancel context.CancelFunc
}

// prober implements Prober
var _ Prober = (*prober)(nil)

// SLI implements Prober
func (p *prober) SLI() (int64, int64) {
	return atomic.LoadInt64(&p.requests), atomic.LoadInt64(&p.failures)
}

// Stop implements Prober
func (p *prober) Stop() error {
	// Wait for either an error to happen or the minimumProbes we want.
	select {
	case <-p.ctx.Done():
	case <-p.minDoneCh:
	}

	// Stop all probing.
	p.cancel()

	return p.errGrp.Wait()
}

// ProberManager is the interface for spawning probers, and checking their results.
type ProberManager interface {
	// The ProberManager should expose a way to collectively reason about spawned
	// probes as a sort of aggregating Prober.
	Prober

	// Spawn creates a new Prober
	Spawn(url *url.URL) Prober

	// Foreach iterates over the probers spawned by this ProberManager.
	Foreach(func(url *url.URL, p Prober))
}

type manager struct {
	// Should not change after creation
	logf      logging.FormatLogger
	clients   *Clients
	minProbes int64

	m                sync.RWMutex
	probes           map[*url.URL]Prober
	transportOptions []spoof.TransportOption
}

var _ ProberManager = (*manager)(nil)

// Spawn implements ProberManager
func (m *manager) Spawn(url *url.URL) Prober {
	m.m.Lock()
	defer m.m.Unlock()

	if p, ok := m.probes[url]; ok {
		return p
	}

	m.logf("Starting Route prober for %s.", url)

	ctx, cancel := context.WithCancel(context.Background())
	errGrp, ctx := errgroup.WithContext(ctx)

	p := &prober{
		logf:          m.logf,
		url:           url,
		minimumProbes: m.minProbes,

		minDoneCh: make(chan struct{}),

		errGrp: errGrp,
		ctx:    ctx,
		cancel: cancel,
	}
	m.probes[url] = p

	errGrp.Go(func() error {
		client, err := pkgTest.NewSpoofingClient(m.clients.KubeClient, m.logf, url.Hostname(), ServingFlags.ResolvableDomain, m.transportOptions...)
		if err != nil {
			return fmt.Errorf("failed to generate client: %w", err)
		}

		req, err := http.NewRequest(http.MethodGet, url.String(), nil)
		if err != nil {
			return fmt.Errorf("failed to generate request: %w", err)
		}

		// We keep polling the domain and accumulate success rates
		// to ultimately establish the SLI and compare to the SLO.
		for {
			select {
			case <-ctx.Done():
				return nil
			default:
				res, err := client.Do(req)
				if atomic.AddInt64(&p.requests, 1) == p.minimumProbes {
					close(p.minDoneCh)
				}
				if err != nil {
					p.logf("%q error: %v", p.url, err)
					atomic.AddInt64(&p.failures, 1)
				} else if res.StatusCode != http.StatusOK {
					p.logf("%q status = %d, want: %d", p.url, res.StatusCode, http.StatusOK)
					p.logf("response: %s", res)
					atomic.AddInt64(&p.failures, 1)
				}
			}
		}
	})
	return p
}

// Stop implements ProberManager
func (m *manager) Stop() error {
	m.m.Lock()
	defer m.m.Unlock()

	m.logf("Stopping all probers")

	errgrp := &errgroup.Group{}
	for _, prober := range m.probes {
		errgrp.Go(prober.Stop)
	}
	return errgrp.Wait()
}

// SLI implements Prober
func (m *manager) SLI() (total int64, failures int64) {
	m.m.RLock()
	defer m.m.RUnlock()
	for _, prober := range m.probes {
		pt, pf := prober.SLI()
		total += pt
		failures += pf
	}
	return
}

// Foreach implements ProberManager
func (m *manager) Foreach(f func(url *url.URL, p Prober)) {
	m.m.RLock()
	defer m.m.RUnlock()

	for url, prober := range m.probes {
		f(url, prober)
	}
}

// NewProberManager creates a new manager for probes.
func NewProberManager(logf logging.FormatLogger, clients *Clients, minProbes int64, opts ...spoof.TransportOption) ProberManager {
	return &manager{
		logf:             logf,
		clients:          clients,
		minProbes:        minProbes,
		probes:           make(map[*url.URL]Prober),
		transportOptions: opts,
	}
}

// RunRouteProber starts a single Prober of the given domain.
func RunRouteProber(logf logging.FormatLogger, clients *Clients, url *url.URL, opts ...spoof.TransportOption) Prober {
	// Default to 10 probes
	pm := NewProberManager(logf, clients, 10, opts...)
	pm.Spawn(url)
	return pm
}

// AssertProberDefault is a helper for stopping the Prober and checking its SLI
// against the default SLO, which requires perfect responses.
// This takes `testing.T` so that it may be used in `defer`.
func AssertProberDefault(t pkgTest.T, p Prober) {
	t.Helper()
	if err := p.Stop(); err != nil {
		t.Error("Stop()", "error", err.Error())
	}
	// Default to 100% correct (typically used in conjunction with the low probe count above)
	if err := CheckSLO(1.0, t.Name(), p); err != nil {
		t.Error("CheckSLO()", "error", err.Error())
	}
}

// CheckSLO compares the SLI of the given prober against the SLO, erroring if too low.
func CheckSLO(slo float64, name string, p Prober) error {
	total, failures := p.SLI()

	successRate := float64(total-failures) / float64(total)
	if successRate < slo {
		return fmt.Errorf("SLI for %q = %f, wanted >= %f", name, successRate, slo)
	}
	return nil
}
<|endoftext|>