<commit_before>package biasgame

import (
	"bytes"
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	"math/rand"
	"regexp"
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bwmarrin/discordgo"
	"github.com/go-redis/redis"
	"github.com/nfnt/resize"
	"github.com/sirupsen/logrus"
)

// giveImageShadowBorder give the round image a shadow border
func giveImageShadowBorder(img image.Image, offsetX int, offsetY int) image.Image {
	rgba := image.NewRGBA(shadowBorder.Bounds())
	draw.Draw(rgba, shadowBorder.Bounds(), shadowBorder, image.Point{0, 0}, draw.Src)
	draw.Draw(rgba, img.Bounds().Add(image.Pt(offsetX, offsetY)), img, image.ZP, draw.Over)
	return rgba.SubImage(rgba.Rect)
}

// bgLog is just a small helper function for logging in the biasgame
func bgLog() *logrus.Entry {
	return cache.GetLogger().WithField("module", "biasgame")
}

// getBiasGameCache
func getBiasGameCache(key string, data interface{}) error {
	// get cache with given key
	cacheResult, err := cache.GetRedisClient().Get(fmt.Sprintf("robyul2-discord:biasgame:%s", key)).Bytes()
	if err != nil || err == redis.Nil {
		return err
	}

	// if the datas type is already []byte then set it to cache instead of unmarshal
	switch data.(type) {
	case []byte:
		data = cacheResult
		return nil
	}

	err = json.Unmarshal(cacheResult, data)
	return err
}

// setBiasGameCache
func setBiasGameCache(key string, data interface{}, time time.Duration) error {
	marshaledData, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, err = cache.GetRedisClient().Set(fmt.Sprintf("robyul2-discord:biasgame:%s", key), marshaledData, time).Result()
	return err
}

// delBiasGameCache
func delBiasGameCache(keys ...string) {
	for _, key := range keys {

		cache.GetRedisClient().Del(fmt.Sprintf("robyul2-discord:biasgame:%s", key)).Result()
	}
}

// getMatchingIdolAndGroup will do a loose comparison of the name and group passed to the ones that already exist
//  1st return is true if group exists
//  2nd return is true if idol exists in the group
//  3rd will be a reference to the matching idol
func getMatchingIdolAndGroup(searchGroup, searchName string) (bool, bool, *biasChoice) {
	groupMatch := false
	nameMatch := false
	var matchingBiasChoice *biasChoice

	// create map of group => idols in group
	groupIdolMap := make(map[string][]*biasChoice)
	for _, bias := range getAllBiases() {
		groupIdolMap[bias.GroupName] = append(groupIdolMap[bias.GroupName], bias)
	}

	// check if the group suggested matches a current group. do loose comparison
	reg, _ := regexp.Compile("[^a-zA-Z0-9]+")
	for k, v := range groupIdolMap {
		curGroup := strings.ToLower(reg.ReplaceAllString(k, ""))
		sugGroup := strings.ToLower(reg.ReplaceAllString(searchGroup, ""))

		// if groups match, set the suggested group to the current group
		if curGroup == sugGroup {
			groupMatch = true

			// check if the idols name matches
			for _, idol := range v {
				curName := strings.ToLower(reg.ReplaceAllString(idol.BiasName, ""))
				sugName := strings.ToLower(reg.ReplaceAllString(searchName, ""))

				if curName == sugName {
					nameMatch = true
					matchingBiasChoice = idol
					break
				}
			}
			break
		}
	}

	return groupMatch, nameMatch, matchingBiasChoice
}

// sendPagedEmbedOfImages takes the given image []byte and sends them in a paged embed
func sendPagedEmbedOfImages(msg *discordgo.Message, imagesToSend []biasImage, displayObjectIds bool, authorName, description string) {
	positionMap := []string{"Top Left", "Top Right", "Bottom Left", "Bottom Right"}

	// create images embed message
	imagesMessage := &discordgo.MessageSend{
		Embed: &discordgo.MessageEmbed{
			Description: description,
			Color:       0x0FADED,
			Author: &discordgo.MessageEmbedAuthor{
				Name: authorName,
			},
			Image:  &discordgo.MessageEmbedImage{},
			Fields: []*discordgo.MessageEmbedField{},
		},
		Files: []*discordgo.File{},
	}

	// loop through images, make a 2x2 collage and set it as a file
	var images [][]byte
	for i, img := range imagesToSend {
		images = append(images, img.getImgBytes())

		if displayObjectIds {

			imagesMessage.Embed.Fields = append(imagesMessage.Embed.Fields, &discordgo.MessageEmbedField{
				Name:  positionMap[i%4],
				Value: img.ObjectName,
			})
		}

		// one page should display 4 images
		if (i+1)%4 == 0 {

			// make collage and set the image as a file in the embed
			collageBytes := helpers.CollageFromBytes(images, []string{}, 300, 300, 150, 150, helpers.DISCORD_DARK_THEME_BACKGROUND_HEX)
			imagesMessage.Files = append(imagesMessage.Files, &discordgo.File{
				Name:   fmt.Sprintf("image%d.png", i),
				Reader: bytes.NewReader(collageBytes),
			})

			// reset images array
			images = make([][]byte, 0)
		}
	}

	// check for any left over images
	if len(images) > 0 {
		// make collage and set the image as a file in the embed
		collageBytes := helpers.CollageFromBytes(images, []string{}, 300, 300, 150, 150, helpers.DISCORD_DARK_THEME_BACKGROUND_HEX)
		imagesMessage.Files = append(imagesMessage.Files, &discordgo.File{
			Name:   fmt.Sprintf("image%d.png", len(imagesMessage.Files)+1),
			Reader: bytes.NewReader(collageBytes),
		})
	}

	// send paged embed
	helpers.SendPagedImageMessage(msg, imagesMessage, 4)
}

// makeVSImage will make the image that shows for rounds in the biasgame
func makeVSImage(img1, img2 image.Image) image.Image {
	// resize images if needed
	if img1.Bounds().Dy() != IMAGE_RESIZE_HEIGHT || img2.Bounds().Dy() != IMAGE_RESIZE_HEIGHT {
		img1 = resize.Resize(0, IMAGE_RESIZE_HEIGHT, img1, resize.Lanczos3)
		img2 = resize.Resize(0, IMAGE_RESIZE_HEIGHT, img2, resize.Lanczos3)
	}

	// give shadow border
	img1 = giveImageShadowBorder(img1, 15, 15)
	img2 = giveImageShadowBorder(img2, 15, 15)

	// combind images
	img1 = helpers.CombineTwoImages(img1, versesImage)
	return helpers.CombineTwoImages(img1, img2)
}

// getAllBiases getter for all biases
func getAllBiases() []*biasChoice {
	allBiasesMutex.RLock()
	defer allBiasesMutex.RUnlock()

	if allBiasChoices == nil {
		return nil
	}

	return allBiasChoices
}

// setAllBiases setter for all biases
func setAllBiases(biases []*biasChoice) {
	allBiasesMutex.Lock()
	defer allBiasesMutex.Unlock()

	allBiasChoices = biases
}

// holds aliases for commands
func isCommandAlias(input, targetCommand string) bool {
	// if input is already the same as target command no need to check aliases
	if input == targetCommand {
		return true
	}

	var aliasMap = map[string]string{
		"images": "images",
		"image":  "images",
		"pic":    "images",
		"pics":   "images",
		"img":    "images",
		"imgs":   "images",

		"image-ids":  "image-ids",
		"images-ids": "image-ids",
		"pic-ids":    "image-ids",
		"pics-ids":   "image-ids",

		"rankings": "rankings",
		"ranking":  "rankings",
		"rank":     "rankings",
		"ranks":    "rankings",

		"current": "current",
		"cur":     "current",

		"multi":       "multi",
		"multiplayer": "multi",

		"server-rankings": "server-rankings",
		"server-ranking":  "server-rankings",
		"server-ranks":    "server-rankings",
		"server-rank":     "server-rankings",
	}

	if attemptedCommand, ok := aliasMap[input]; ok {
		return attemptedCommand == targetCommand
	}

	return false
}

// <3
func getRandomNayoungEmoji() string {
	nayoungEmojiArray := []string{
		":nayoungthumbsup:430592739839705091",
		":nayoungsalute:430592737340030979",
		":nayounghype:430592740066066433",
		":nayoungheart6:430592739868934164",
		":nayoungheart2:430592737004224514",
		":nayoungheart:430592736496713738",
		":nayoungok:424683077793611777",
		"a:anayoungminnie:430592552610299924",
	}

	randomIndex := rand.Intn(len(nayoungEmojiArray))
	return nayoungEmojiArray[randomIndex]
}

// checks if the error is a permissions error and notifies the user
func checkPermissionError(err error, channelID string) {
	if err == nil {
		return
	}

	// check if error is a permissions error
	if err, ok := err.(*discordgo.RESTError); ok && err.Message.Code == discordgo.ErrCodeMissingPermissions {
		helpers.SendMessage(channelID, helpers.GetText("bot.errors.no-file"))
	}
}
<commit_msg>[biasgame] fixes issue with handling incomplete errors sent from discord<commit_after>package biasgame

import (
	"bytes"
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	"math/rand"
	"regexp"
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bwmarrin/discordgo"
	"github.com/go-redis/redis"
	"github.com/nfnt/resize"
	"github.com/sirupsen/logrus"
)

// giveImageShadowBorder give the round image a shadow border
func giveImageShadowBorder(img image.Image, offsetX int, offsetY int) image.Image {
	rgba := image.NewRGBA(shadowBorder.Bounds())
	draw.Draw(rgba, shadowBorder.Bounds(), shadowBorder, image.Point{0, 0}, draw.Src)
	draw.Draw(rgba, img.Bounds().Add(image.Pt(offsetX, offsetY)), img, image.ZP, draw.Over)
	return rgba.SubImage(rgba.Rect)
}

// bgLog is just a small helper function for logging in the biasgame
func bgLog() *logrus.Entry {
	return cache.GetLogger().WithField("module", "biasgame")
}

// getBiasGameCache
func getBiasGameCache(key string, data interface{}) error {
	// get cache with given key
	cacheResult, err := cache.GetRedisClient().Get(fmt.Sprintf("robyul2-discord:biasgame:%s", key)).Bytes()
	if err != nil || err == redis.Nil {
		return err
	}

	// if the datas type is already []byte then set it to cache instead of unmarshal
	switch data.(type) {
	case []byte:
		data = cacheResult
		return nil
	}

	err = json.Unmarshal(cacheResult, data)
	return err
}

// setBiasGameCache
func setBiasGameCache(key string, data interface{}, time time.Duration) error {
	marshaledData, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, err = cache.GetRedisClient().Set(fmt.Sprintf("robyul2-discord:biasgame:%s", key), marshaledData, time).Result()
	return err
}

// delBiasGameCache
func delBiasGameCache(keys ...string) {
	for _, key := range keys {

		cache.GetRedisClient().Del(fmt.Sprintf("robyul2-discord:biasgame:%s", key)).Result()
	}
}

// getMatchingIdolAndGroup will do a loose comparison of the name and group passed to the ones that already exist
//  1st return is true if group exists
//  2nd return is true if idol exists in the group
//  3rd will be a reference to the matching idol
func getMatchingIdolAndGroup(searchGroup, searchName string) (bool, bool, *biasChoice) {
	groupMatch := false
	nameMatch := false
	var matchingBiasChoice *biasChoice

	// create map of group => idols in group
	groupIdolMap := make(map[string][]*biasChoice)
	for _, bias := range getAllBiases() {
		groupIdolMap[bias.GroupName] = append(groupIdolMap[bias.GroupName], bias)
	}

	// check if the group suggested matches a current group. do loose comparison
	reg, _ := regexp.Compile("[^a-zA-Z0-9]+")
	for k, v := range groupIdolMap {
		curGroup := strings.ToLower(reg.ReplaceAllString(k, ""))
		sugGroup := strings.ToLower(reg.ReplaceAllString(searchGroup, ""))

		// if groups match, set the suggested group to the current group
		if curGroup == sugGroup {
			groupMatch = true

			// check if the idols name matches
			for _, idol := range v {
				curName := strings.ToLower(reg.ReplaceAllString(idol.BiasName, ""))
				sugName := strings.ToLower(reg.ReplaceAllString(searchName, ""))

				if curName == sugName {
					nameMatch = true
					matchingBiasChoice = idol
					break
				}
			}
			break
		}
	}

	return groupMatch, nameMatch, matchingBiasChoice
}

// sendPagedEmbedOfImages takes the given image []byte and sends them in a paged embed
func sendPagedEmbedOfImages(msg *discordgo.Message, imagesToSend []biasImage, displayObjectIds bool, authorName, description string) {
	positionMap := []string{"Top Left", "Top Right", "Bottom Left", "Bottom Right"}

	// create images embed message
	imagesMessage := &discordgo.MessageSend{
		Embed: &discordgo.MessageEmbed{
			Description: description,
			Color:       0x0FADED,
			Author: &discordgo.MessageEmbedAuthor{
				Name: authorName,
			},
			Image:  &discordgo.MessageEmbedImage{},
			Fields: []*discordgo.MessageEmbedField{},
		},
		Files: []*discordgo.File{},
	}

	// loop through images, make a 2x2 collage and set it as a file
	var images [][]byte
	for i, img := range imagesToSend {
		images = append(images, img.getImgBytes())

		if displayObjectIds {

			imagesMessage.Embed.Fields = append(imagesMessage.Embed.Fields, &discordgo.MessageEmbedField{
				Name:  positionMap[i%4],
				Value: img.ObjectName,
			})
		}

		// one page should display 4 images
		if (i+1)%4 == 0 {

			// make collage and set the image as a file in the embed
			collageBytes := helpers.CollageFromBytes(images, []string{}, 300, 300, 150, 150, helpers.DISCORD_DARK_THEME_BACKGROUND_HEX)
			imagesMessage.Files = append(imagesMessage.Files, &discordgo.File{
				Name:   fmt.Sprintf("image%d.png", i),
				Reader: bytes.NewReader(collageBytes),
			})

			// reset images array
			images = make([][]byte, 0)
		}
	}

	// check for any left over images
	if len(images) > 0 {
		// make collage and set the image as a file in the embed
		collageBytes := helpers.CollageFromBytes(images, []string{}, 300, 300, 150, 150, helpers.DISCORD_DARK_THEME_BACKGROUND_HEX)
		imagesMessage.Files = append(imagesMessage.Files, &discordgo.File{
			Name:   fmt.Sprintf("image%d.png", len(imagesMessage.Files)+1),
			Reader: bytes.NewReader(collageBytes),
		})
	}

	// send paged embed
	helpers.SendPagedImageMessage(msg, imagesMessage, 4)
}

// makeVSImage will make the image that shows for rounds in the biasgame
func makeVSImage(img1, img2 image.Image) image.Image {
	// resize images if needed
	if img1.Bounds().Dy() != IMAGE_RESIZE_HEIGHT || img2.Bounds().Dy() != IMAGE_RESIZE_HEIGHT {
		img1 = resize.Resize(0, IMAGE_RESIZE_HEIGHT, img1, resize.Lanczos3)
		img2 = resize.Resize(0, IMAGE_RESIZE_HEIGHT, img2, resize.Lanczos3)
	}

	// give shadow border
	img1 = giveImageShadowBorder(img1, 15, 15)
	img2 = giveImageShadowBorder(img2, 15, 15)

	// combind images
	img1 = helpers.CombineTwoImages(img1, versesImage)
	return helpers.CombineTwoImages(img1, img2)
}

// getAllBiases getter for all biases
func getAllBiases() []*biasChoice {
	allBiasesMutex.RLock()
	defer allBiasesMutex.RUnlock()

	if allBiasChoices == nil {
		return nil
	}

	return allBiasChoices
}

// setAllBiases setter for all biases
func setAllBiases(biases []*biasChoice) {
	allBiasesMutex.Lock()
	defer allBiasesMutex.Unlock()

	allBiasChoices = biases
}

// holds aliases for commands
func isCommandAlias(input, targetCommand string) bool {
	// if input is already the same as target command no need to check aliases
	if input == targetCommand {
		return true
	}

	var aliasMap = map[string]string{
		"images": "images",
		"image":  "images",
		"pic":    "images",
		"pics":   "images",
		"img":    "images",
		"imgs":   "images",

		"image-ids":  "image-ids",
		"images-ids": "image-ids",
		"pic-ids":    "image-ids",
		"pics-ids":   "image-ids",

		"rankings": "rankings",
		"ranking":  "rankings",
		"rank":     "rankings",
		"ranks":    "rankings",

		"current": "current",
		"cur":     "current",

		"multi":       "multi",
		"multiplayer": "multi",

		"server-rankings": "server-rankings",
		"server-ranking":  "server-rankings",
		"server-ranks":    "server-rankings",
		"server-rank":     "server-rankings",
	}

	if attemptedCommand, ok := aliasMap[input]; ok {
		return attemptedCommand == targetCommand
	}

	return false
}

// <3
func getRandomNayoungEmoji() string {
	nayoungEmojiArray := []string{
		":nayoungthumbsup:430592739839705091",
		":nayoungsalute:430592737340030979",
		":nayounghype:430592740066066433",
		":nayoungheart6:430592739868934164",
		":nayoungheart2:430592737004224514",
		":nayoungheart:430592736496713738",
		":nayoungok:424683077793611777",
		"a:anayoungminnie:430592552610299924",
	}

	randomIndex := rand.Intn(len(nayoungEmojiArray))
	return nayoungEmojiArray[randomIndex]
}

// checks if the error is a permissions error and notifies the user
func checkPermissionError(err error, channelID string) {
	if err == nil {
		return
	}

	// check if error is a permissions error
	if err, ok := err.(*discordgo.RESTError); ok && err.Message != nil {
		if err.Message.Code == discordgo.ErrCodeMissingPermissions {
			helpers.SendMessage(channelID, helpers.GetText("bot.errors.no-file"))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"os"

	log "github.com/cihub/seelog"
	"github.com/facebookgo/inject"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/logic"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/metrics"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/protocol"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/websocket"
)

//TODO make config general by using a map so we can get config from ENV,file or flag.
type ServerConfig struct {
	NodePort         string
	WebPort          string
	WebRoot          string
	ElasticSearch    string
	InfluxDbServer   string
	InfluxDbUser     string
	InfluxDbPassword string
}

type Startable interface {
	Start()
}

func main() {

	config := &ServerConfig{}

	flag.StringVar(&config.NodePort, "node-port", "8282", "Stampzilla NodeServer port")
	flag.StringVar(&config.WebPort, "web-port", "8080", "Webserver port")
	flag.StringVar(&config.WebRoot, "web-root", "public", "Webserver root")
	flag.StringVar(&config.ElasticSearch, "elasticsearch", "", "Address to an ElasticSearch host. Ex: http://hostname:9200/test/test")
	flag.StringVar(&config.InfluxDbServer, "influxdbserver", "", "Address to an InfluxDb host. Ex: http://localhost:8086")
	flag.StringVar(&config.InfluxDbUser, "influxdbuser", "", "InfluxDb user. ")
	flag.StringVar(&config.InfluxDbPassword, "influxdbpassword", "", "InfluxDb password. ")
	flag.Parse()

	getConfigFromEnv(config)
	readConfigFromFile("config.json", config)

	// Load logger
	logger, err := log.LoggerFromConfigAsFile("logconfig.xml")
	if err != nil {
		testConfig := `
			<seelog type="sync" asyncinterval="1000" minlevel="trace">
				<outputs>
					<filter levels="trace">
						<console formatid="colored-trace"/>
					</filter>
					<filter levels="debug">
						<console formatid="colored-debug"/>
					</filter>
					<filter levels="info">
						<console formatid="colored-info"/>
					</filter>
					<filter levels="warn">
						<console formatid="colored-warn"/>
					</filter>
					<filter levels="error">
						<console formatid="colored-error"/>
					</filter>
					<filter levels="critical">
						<console formatid="colored-critical"/>
					</filter>
				</outputs>
				<formats>
					<format id="colored-trace"  format="%Date %Time %EscM(40)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-debug"  format="%Date %Time %EscM(45)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-info"  format="%Date %Time %EscM(46)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-warn"  format="%Date %Time %EscM(43)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-error"  format="%Date %Time %EscM(41)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-critical"  format="%Date %Time %EscM(41)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
				</formats>
			</seelog>`
		logger, _ = log.LoggerFromConfigAsBytes([]byte(testConfig))
	}
	log.ReplaceLogger(logger)

	services := make([]interface{}, 0)

	// Register metrics loggers
	if config.ElasticSearch != "" {
		log.Info("Starting ElasticSearch metrics logger")
		es := NewElasticSearch()
		services = append(services, es)
	}
	if config.InfluxDbServer != "" {
		i := NewInfluxDb()
		services = append(services, i)
	}

	// Register the rest of the services
	services = append(services, &WebsocketHandler{}, config, protocol.NewNodes(), logic.NewLogic(), logic.NewScheduler(), websocket.NewRouter(), NewNodeServer(), NewWebServer())

	//Add metrics service if we have any loggers (Elasticsearch, influxdb, graphite etc)
	if loggers := getLoggers(services); len(loggers) != 0 {
		m := metrics.New()
		log.Info("Detected metrics loggers, starting up")
		for _, l := range loggers {
			m.AddLogger(l)
		}
		services = append(services, m)
	}

	err = inject.Populate(services...)
	if err != nil {
		panic(err)
	}

	saveConfigToFile(config)

	StartServices(services)
	select {}
}

func StartServices(services []interface{}) {
	for _, s := range services {
		if s, ok := s.(Startable); ok {
			s.Start()
		}

	}
}

func getLoggers(services []interface{}) []metrics.Logger {
	var loggers []metrics.Logger
	for _, s := range services {
		if s, ok := s.(metrics.Logger); ok {
			loggers = append(loggers, s)
		}
	}
	return loggers
}

func getConfigFromEnv(config *ServerConfig) {

	//TODO make prettier and generate from map with both ENV and flags
	if val := os.Getenv("STAMPZILLA_WEBROOT"); val != "" {
		config.WebRoot = val
	}
}

func readConfigFromFile(fn string, config *ServerConfig) {
	configFile, err := os.Open(fn)
	if err != nil {
		log.Error("opening config file", err.Error())
		return
	}

	newConfig := &ServerConfig{}
	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&config); err != nil {
		log.Error("parsing config file", err.Error())
	}

	if newConfig.InfluxDbServer != "" {
		config.InfluxDbServer = newConfig.InfluxDbServer
	}
}

func saveConfigToFile(config *ServerConfig) {
	configFile, err := os.Create("config.json")
	if err != nil {
		log.Error("creating config file", err.Error())
	}

	log.Info("Save config: ", config)
	var out bytes.Buffer
	b, err := json.MarshalIndent(config, "", "\t")
	if err != nil {
		log.Error("error marshal json", err)
	}
	json.Indent(&out, b, "", "\t")
	out.WriteTo(configFile)
}
<commit_msg>fixed #9 and an issue with config from environment getting overwritten by config file<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"os"

	log "github.com/cihub/seelog"
	"github.com/facebookgo/inject"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/logic"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/metrics"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/protocol"
	"github.com/stampzilla/stampzilla-go/nodes/stampzilla-server/websocket"
)

//TODO make config general by using a map so we can get config from ENV,file or flag.
type ServerConfig struct {
	NodePort         string
	WebPort          string
	WebRoot          string
	ElasticSearch    string
	InfluxDbServer   string
	InfluxDbUser     string
	InfluxDbPassword string
}

type Startable interface {
	Start()
}

func main() {

	config := &ServerConfig{}

	flag.StringVar(&config.NodePort, "node-port", "8282", "Stampzilla NodeServer port")
	flag.StringVar(&config.WebPort, "web-port", "8080", "Webserver port")
	flag.StringVar(&config.WebRoot, "web-root", "public", "Webserver root")
	flag.StringVar(&config.ElasticSearch, "elasticsearch", "", "Address to an ElasticSearch host. Ex: http://hostname:9200/test/test")
	flag.StringVar(&config.InfluxDbServer, "influxdbserver", "", "Address to an InfluxDb host. Ex: http://localhost:8086")
	flag.StringVar(&config.InfluxDbUser, "influxdbuser", "", "InfluxDb user. ")
	flag.StringVar(&config.InfluxDbPassword, "influxdbpassword", "", "InfluxDb password. ")
	flag.Parse()

	readConfigFromFile("config.json", config)
	getConfigFromEnv(config)

	// Load logger
	logger, err := log.LoggerFromConfigAsFile("logconfig.xml")
	if err != nil {
		testConfig := `
			<seelog type="sync" asyncinterval="1000" minlevel="trace">
				<outputs>
					<filter levels="trace">
						<console formatid="colored-trace"/>
					</filter>
					<filter levels="debug">
						<console formatid="colored-debug"/>
					</filter>
					<filter levels="info">
						<console formatid="colored-info"/>
					</filter>
					<filter levels="warn">
						<console formatid="colored-warn"/>
					</filter>
					<filter levels="error">
						<console formatid="colored-error"/>
					</filter>
					<filter levels="critical">
						<console formatid="colored-critical"/>
					</filter>
				</outputs>
				<formats>
					<format id="colored-trace"  format="%Date %Time %EscM(40)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-debug"  format="%Date %Time %EscM(45)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-info"  format="%Date %Time %EscM(46)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-warn"  format="%Date %Time %EscM(43)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-error"  format="%Date %Time %EscM(41)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
					<format id="colored-critical"  format="%Date %Time %EscM(41)%Level%EscM(49) - %File:%Line - %Msg%n%EscM(0)"/>
				</formats>
			</seelog>`
		logger, _ = log.LoggerFromConfigAsBytes([]byte(testConfig))
	}
	log.ReplaceLogger(logger)

	services := make([]interface{}, 0)

	// Register metrics loggers
	if config.ElasticSearch != "" {
		log.Info("Starting ElasticSearch metrics logger")
		es := NewElasticSearch()
		services = append(services, es)
	}
	if config.InfluxDbServer != "" {
		i := NewInfluxDb()
		services = append(services, i)
	}

	// Register the rest of the services
	services = append(services, &WebsocketHandler{}, config, protocol.NewNodes(), logic.NewLogic(), logic.NewScheduler(), websocket.NewRouter(), NewNodeServer(), NewWebServer())

	//Add metrics service if we have any loggers (Elasticsearch, influxdb, graphite etc)
	if loggers := getLoggers(services); len(loggers) != 0 {
		m := metrics.New()
		log.Info("Detected metrics loggers, starting up")
		for _, l := range loggers {
			m.AddLogger(l)
		}
		services = append(services, m)
	}

	err = inject.Populate(services...)
	if err != nil {
		panic(err)
	}

	saveConfigToFile(config)

	StartServices(services)
	select {}
}

func StartServices(services []interface{}) {
	for _, s := range services {
		if s, ok := s.(Startable); ok {
			s.Start()
		}

	}
}

func getLoggers(services []interface{}) []metrics.Logger {
	var loggers []metrics.Logger
	for _, s := range services {
		if s, ok := s.(metrics.Logger); ok {
			loggers = append(loggers, s)
		}
	}
	return loggers
}

func getConfigFromEnv(config *ServerConfig) {

	//TODO make prettier and generate from map with both ENV and flags
	if val := os.Getenv("STAMPZILLA_WEBROOT"); val != "" {
		config.WebRoot = val
	}
}

func readConfigFromFile(fn string, config *ServerConfig) {
	configFile, err := os.Open(fn)
	if err != nil {
		log.Error("opening config file", err.Error())
		return
	}

	newConfig := &ServerConfig{}
	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&newConfig); err != nil {
		log.Error("parsing config file", err.Error())
	}

	//Command line arguments has higher priority. Only implemented for config.InfluxDbServer yet
	//TODO generalize using reflect to itearate over config struct so check all
	if config.InfluxDbServer != "" {
		log.Info("config.InfluxDbServer != \"\"")
		newConfig.InfluxDbServer = config.InfluxDbServer
	}

	*config = *newConfig
}

func saveConfigToFile(config *ServerConfig) {
	configFile, err := os.Create("config.json")
	if err != nil {
		log.Error("creating config file", err.Error())
	}

	log.Info("Save config: ", config)
	var out bytes.Buffer
	b, err := json.MarshalIndent(config, "", "\t")
	if err != nil {
		log.Error("error marshal json", err)
	}
	json.Indent(&out, b, "", "\t")
	out.WriteTo(configFile)
}
<|endoftext|>


<commit_before>package forum

import (
    "testing"
    "time"
    "math/rand"
    "fmt"
    "github.com/carbocation/util.git/datatypes/binarytree"
)

func TestPopulation(t *testing.T) {
    // Make some sample entries based on a skeleton; the Id's will be appropriately distinct.
    entries := []Entry{
        Entry{ Id: 3905, Title: "Hello, world title.", Body: "This is a basic body.", Created: time.Now(), AuthorId: 1},
        Entry{ Id: 3906, Title: "Frost Psot", Body: "This is a spam post.", Created: time.Now(), AuthorId: 2},
        Entry{ Id: 3907, Title: "Third post", Body: "I want to bury the spam.", Created: time.Now(), AuthorId: 3},
        Entry{ Id: 3908, Title: "Les Mis", Body: "It's being shown on the Oscars now.", Created: time.Now(), AuthorId: 3},
        Entry{ Id: 3909, Title: "LOOL", Body: "Why are you watching those?", Created: time.Now(), AuthorId: 2},
        Entry{ Id: 3910, Title: "Too bad", Body: "I'm here to resurrect the spam.", Created: time.Now(), AuthorId: 2},
    }

    // Create a closure table to represent the relationships among the entries
    // In reality, you'd probably directly import the closure table data into the ClosureTable class
    closuretable := ClosureTable{Relationship{Ancestor: 3905, Descendant: 3905, Depth: 0}}
    closuretable.AddChild(Child{Parent: 3905, Child: 3906})
    closuretable.AddChild(Child{Parent: 3905, Child: 3907})
    closuretable.AddChild(Child{Parent: 3907, Child: 3908})
    closuretable.AddChild(Child{Parent: 3908, Child: 3909})
    closuretable.AddChild(Child{Parent: 3905, Child: 3910})

    //Build a tree out of the entries based on the closure table's instructions.
    tree := closuretable.TableToTree(entries)

    fmt.Println(walkBody(tree))
}

func walkBody(el *binarytree.Tree) string {
    if el == nil {
        return ""
    }

    out := ""
    out += el.Value.(Entry).Body
    out += walkBody(el.Left())
    out += walkBody(el.Right())

    return out
}

func buildClosureTable(N int) ClosureTable {
    // Create the closure table with a single progenitor
    ct := ClosureTable{Relationship{Ancestor: 0, Descendant: 0, Depth: 0}}

    for i := 1; i < N; i++ {
        // Create a place for entry #i, making it the child of a random entry j<i
        err := ct.AddChild(Child{Parent: rand.Int63n(int64(i)), Child: int64(i)})
        if err != nil {
            fmt.Println(err)
            break
        }
    }

    return ct
}<commit_msg>Test now passes.<commit_after>package forum

import (
    "testing"
    "time"
    "math/rand"
    "fmt"
    "github.com/carbocation/util.git/datatypes/binarytree"
)

func TestClosureConversion(t *testing.T) {
    // Make some sample entries based on a skeleton; the Id's will be appropriately distinct.
    entries := []Entry{
        Entry{ Id: 3905, Title: "Hello, world title.", Body: "This is a basic body.", Created: time.Now(), AuthorId: 1},
        Entry{ Id: 3906, Title: "Frost Psot", Body: "This is a spam post.", Created: time.Now(), AuthorId: 2},
        Entry{ Id: 3907, Title: "Third post", Body: "I want to bury the spam.", Created: time.Now(), AuthorId: 3},
        Entry{ Id: 3908, Title: "Les Mis", Body: "It's being shown on the Oscars now.", Created: time.Now(), AuthorId: 3},
        Entry{ Id: 3909, Title: "LOOL", Body: "Why are you watching those?", Created: time.Now(), AuthorId: 2},
        Entry{ Id: 3910, Title: "Too bad", Body: "I'm here to resurrect the spam.", Created: time.Now(), AuthorId: 2},
    }

    // Create a closure table to represent the relationships among the entries
    // In reality, you'd probably directly import the closure table data into the ClosureTable class
    closuretable := ClosureTable{Relationship{Ancestor: 3905, Descendant: 3905, Depth: 0}}
    closuretable.AddChild(Child{Parent: 3905, Child: 3906})
    closuretable.AddChild(Child{Parent: 3905, Child: 3907})
    closuretable.AddChild(Child{Parent: 3907, Child: 3908})
    closuretable.AddChild(Child{Parent: 3908, Child: 3909})
    closuretable.AddChild(Child{Parent: 3905, Child: 3910})

    //Build a tree out of the entries based on the closure table's instructions.
    tree := walkBody(closuretable.TableToTree(entries))
    expected := "This is a basic body.This is a spam post.I want to bury the spam.It's being shown on the Oscars now.Why are you watching those?I'm here to resurrect the spam."

    if tree != expected {
        t.Errorf("walkBody(tree) yielded %s, expected %s. Have you made a change that caused the iteration order to become indeterminate, e.g., using a map instead of a slice?", tree, expected)
    }
}

func walkBody(el *binarytree.Tree) string {
    if el == nil {
        return ""
    }

    out := ""
    out += el.Value.(Entry).Body
    out += walkBody(el.Left())
    out += walkBody(el.Right())

    return out
}

func buildClosureTable(N int) ClosureTable {
    // Create the closure table with a single progenitor
    ct := ClosureTable{Relationship{Ancestor: 0, Descendant: 0, Depth: 0}}

    for i := 1; i < N; i++ {
        // Create a place for entry #i, making it the child of a random entry j<i
        err := ct.AddChild(Child{Parent: rand.Int63n(int64(i)), Child: int64(i)})
        if err != nil {
            fmt.Println(err)
            break
        }
    }

    return ct
}<|endoftext|>


<commit_before>package sourcegraph

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"

	"github.com/google/go-querystring/query"
	muxpkg "github.com/sqs/mux"
	"sourcegraph.com/sourcegraph/go-sourcegraph/router"
)

const (
	libraryVersion = "0.0.1"
	userAgent      = "sourcegraph-client/" + libraryVersion
)

// A Client communicates with the Sourcegraph API.
type Client struct {
	// Services used to communicate with different parts of the Sourcegraph API.
	BuildData    BuildDataService
	Builds       BuildsService
	Deltas       DeltasService
	Issues       IssuesService
	Orgs         OrgsService
	People       PeopleService
	PullRequests PullRequestsService
	Repos        ReposService
	RepoTree     RepoTreeService
	Search       SearchService
	Units        UnitsService
	Users        UsersService
	Defs         DefsService

	// Base URL for API requests, which should have a trailing slash.
	BaseURL *url.URL

	// User agent used for HTTP requests to the Sourcegraph API.
	UserAgent string

	// HTTP client used to communicate with the Sourcegraph API.
	httpClient *http.Client
}

// NewClient returns a new Sourcegraph API client. If httpClient is nil,
// http.DefaultClient is used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		cloned := *http.DefaultClient
		httpClient = &cloned
	}

	c := new(Client)
	c.httpClient = httpClient
	c.BuildData = &buildDataService{c}
	c.Builds = &buildsService{c}
	c.Deltas = &deltasService{c}
	c.Issues = &issuesService{c}
	c.Orgs = &orgsService{c}
	c.People = &peopleService{c}
	c.PullRequests = &pullRequestsService{c}
	c.Repos = &repositoriesService{c}
	c.RepoTree = &repoTreeService{c}
	c.Search = &searchService{c}
	c.Units = &unitsService{c}
	c.Users = &usersService{c}
	c.Defs = &defsService{c}

	c.BaseURL = &url.URL{Scheme: "https", Host: "sourcegraph.com", Path: "/api/"}

	c.UserAgent = userAgent

	return c
}

// apiRouter is used to generate URLs for the Sourcegraph API.
var apiRouter *muxpkg.Router

// ResetRouter clears and reconstructs the preinitialized API
// router. It should be called after setting an router.ExtraConfig
// func but only during init time.
func ResetRouter() {
	apiRouter = router.NewAPIRouter(nil)
}

func init() { ResetRouter() }

// URL generates the URL to the named Sourcegraph API endpoint, using the
// specified route variables and query options.
func (c *Client) URL(apiRouteName string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	route := apiRouter.Get(apiRouteName)
	if route == nil {
		return nil, fmt.Errorf("no API route named %q", apiRouteName)
	}

	routeVarsList := make([]string, 2*len(routeVars))
	i := 0
	for name, val := range routeVars {
		routeVarsList[i*2] = name
		routeVarsList[i*2+1] = val
		i++
	}
	url, err := route.URL(routeVarsList...)
	if err != nil {
		return nil, err
	}

	// make the route URL path relative to BaseURL by trimming the leading "/"
	url.Path = strings.TrimPrefix(url.Path, "/")

	if opt != nil {
		err = addOptions(url, opt)
		if err != nil {
			return nil, err
		}
	}

	return url, nil
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. Relative
// URLs should always be specified without a preceding slash. If specified, the
// value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *HTTPResponse {
	return &HTTPResponse{Response: r}
}

// HTTPResponse is a wrapped HTTP response from the Sourcegraph API with
// additional Sourcegraph-specific response information parsed out. It
// implements Response.
type HTTPResponse struct {
	*http.Response
}

// TotalCount implements Response.
func (r *HTTPResponse) TotalCount() int {
	tc := r.Header.Get("x-total-count")
	if tc == "" {
		return -1
	}
	n, err := strconv.Atoi(tc)
	if err != nil {
		return -1
	}
	return n
}

type MockResponse struct{}

// Response is a response from the Sourcegraph API. When using the HTTP API,
// API methods return *HTTPResponse values that implement Response.
type Response interface {
	// TotalCount is the total number of items in the resource or result set
	// that exist remotely. Only a portion of the total may be in the response
	// body. If the endpoint did not return a total count, then TotalCount
	// returns -1.
	TotalCount() int
}

// ListOptions specifies general pagination options for fetching a list of
// results.
type ListOptions struct {
	PerPage int `url:",omitempty" json:",omitempty"`
	Page    int `url:",omitempty" json:",omitempty"`
}

const DefaultPerPage = 10

func (o ListOptions) PageOrDefault() int {
	if o.Page <= 0 {
		return 1
	}
	return o.Page
}

func (o ListOptions) PerPageOrDefault() int {
	if o.PerPage <= 0 {
		return DefaultPerPage
	}
	return o.PerPage
}

// Limit returns the number of items to fetch.
func (o ListOptions) Limit() int { return o.PerPageOrDefault() }

// Offset returns the 0-indexed offset of the first item that appears on this
// page, based on the PerPage and Page values (which are given default values if
// they are zero).
func (o ListOptions) Offset() int {
	return (o.PageOrDefault() - 1) * o.PerPageOrDefault()
}

type doKey int // sentinel value type for (*Client).Do v parameter

const preserveBody doKey = iota // when passed as v to (*Client).Do, the resp body is neither parsed nor closed

// Do sends an API request and returns the API response.  The API
// response is decoded and stored in the value pointed to by v, or
// returned as an error if an API error has occurred. If v is
// preserveBody, then the HTTP response body is not closed by Do; the
// caller is responsible for closing it.
func (c *Client) Do(req *http.Request, v interface{}) (*HTTPResponse, error) {
	var resp *HTTPResponse
	rawResp, err := c.httpClient.Do(req)
	if rawResp != nil {
		if v != preserveBody {
			defer rawResp.Body.Close()
		}
		resp = newResponse(rawResp)
		if err == nil {
			// Don't clobber error from Do, if any (it could be, e.g.,
			// a sentinel error returned by the HTTP client's
			// CheckRedirect func).
			if err := CheckResponse(rawResp); err != nil {
				// even though there was an error, we still return the response
				// in case the caller wants to inspect it further
				return resp, err
			}
		}
	}
	if err != nil {
		return resp, err
	}

	if v != nil {
		if bp, ok := v.(*[]byte); ok {
			*bp, err = ioutil.ReadAll(rawResp.Body)
		} else if v != preserveBody {
			err = json.NewDecoder(rawResp.Body).Decode(v)
		}
	}
	if err != nil {
		return resp, fmt.Errorf("error reading response from %s %s: %s", req.Method, req.URL.RequestURI(), err)
	}
	return resp, nil
}

// addOptions adds the parameters in opt as URL query parameters to u. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(u *url.URL, opt interface{}) error {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}

	qs, err := query.Values(opt)
	if err != nil {
		return err
	}

	u.RawQuery = qs.Encode()
	return nil
}

// NewMockClient returns a mockable Client for use in tests.
func NewMockClient() *Client {
	return &Client{
		BuildData:    &MockBuildDataService{},
		Builds:       &MockBuildsService{},
		Deltas:       &MockDeltasService{},
		Issues:       &MockIssuesService{},
		Orgs:         &MockOrgsService{},
		People:       &MockPeopleService{},
		PullRequests: &MockPullRequestsService{},
		Repos:        &MockReposService{},
		RepoTree:     &MockRepoTreeService{},
		Search:       &MockSearchService{},
		Units:        &MockUnitsService{},
		Users:        &MockUsersService{},
		Defs:         &MockDefsService{},
	}
}
<commit_msg>export API router in client package<commit_after>package sourcegraph

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"

	"github.com/google/go-querystring/query"
	"sourcegraph.com/sourcegraph/go-sourcegraph/router"
)

const (
	libraryVersion = "0.0.1"
	userAgent      = "sourcegraph-client/" + libraryVersion
)

// A Client communicates with the Sourcegraph API.
type Client struct {
	// Services used to communicate with different parts of the Sourcegraph API.
	BuildData    BuildDataService
	Builds       BuildsService
	Deltas       DeltasService
	Issues       IssuesService
	Orgs         OrgsService
	People       PeopleService
	PullRequests PullRequestsService
	Repos        ReposService
	RepoTree     RepoTreeService
	Search       SearchService
	Units        UnitsService
	Users        UsersService
	Defs         DefsService

	// Base URL for API requests, which should have a trailing slash.
	BaseURL *url.URL

	// User agent used for HTTP requests to the Sourcegraph API.
	UserAgent string

	// HTTP client used to communicate with the Sourcegraph API.
	httpClient *http.Client
}

// NewClient returns a new Sourcegraph API client. If httpClient is nil,
// http.DefaultClient is used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		cloned := *http.DefaultClient
		httpClient = &cloned
	}

	c := new(Client)
	c.httpClient = httpClient
	c.BuildData = &buildDataService{c}
	c.Builds = &buildsService{c}
	c.Deltas = &deltasService{c}
	c.Issues = &issuesService{c}
	c.Orgs = &orgsService{c}
	c.People = &peopleService{c}
	c.PullRequests = &pullRequestsService{c}
	c.Repos = &repositoriesService{c}
	c.RepoTree = &repoTreeService{c}
	c.Search = &searchService{c}
	c.Units = &unitsService{c}
	c.Users = &usersService{c}
	c.Defs = &defsService{c}

	c.BaseURL = &url.URL{Scheme: "https", Host: "sourcegraph.com", Path: "/api/"}

	c.UserAgent = userAgent

	return c
}

// Router is used to generate URLs for the Sourcegraph API.
var Router = router.NewAPIRouter(nil)

// ResetRouter clears and reconstructs the preinitialized API
// router. It should be called after setting an router.ExtraConfig
// func but only during init time.
func ResetRouter() {
	Router = router.NewAPIRouter(nil)
}

// URL generates the URL to the named Sourcegraph API endpoint, using the
// specified route variables and query options.
func (c *Client) URL(apiRouteName string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	route := Router.Get(apiRouteName)
	if route == nil {
		return nil, fmt.Errorf("no API route named %q", apiRouteName)
	}

	routeVarsList := make([]string, 2*len(routeVars))
	i := 0
	for name, val := range routeVars {
		routeVarsList[i*2] = name
		routeVarsList[i*2+1] = val
		i++
	}
	url, err := route.URL(routeVarsList...)
	if err != nil {
		return nil, err
	}

	// make the route URL path relative to BaseURL by trimming the leading "/"
	url.Path = strings.TrimPrefix(url.Path, "/")

	if opt != nil {
		err = addOptions(url, opt)
		if err != nil {
			return nil, err
		}
	}

	return url, nil
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. Relative
// URLs should always be specified without a preceding slash. If specified, the
// value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *HTTPResponse {
	return &HTTPResponse{Response: r}
}

// HTTPResponse is a wrapped HTTP response from the Sourcegraph API with
// additional Sourcegraph-specific response information parsed out. It
// implements Response.
type HTTPResponse struct {
	*http.Response
}

// TotalCount implements Response.
func (r *HTTPResponse) TotalCount() int {
	tc := r.Header.Get("x-total-count")
	if tc == "" {
		return -1
	}
	n, err := strconv.Atoi(tc)
	if err != nil {
		return -1
	}
	return n
}

type MockResponse struct{}

// Response is a response from the Sourcegraph API. When using the HTTP API,
// API methods return *HTTPResponse values that implement Response.
type Response interface {
	// TotalCount is the total number of items in the resource or result set
	// that exist remotely. Only a portion of the total may be in the response
	// body. If the endpoint did not return a total count, then TotalCount
	// returns -1.
	TotalCount() int
}

// ListOptions specifies general pagination options for fetching a list of
// results.
type ListOptions struct {
	PerPage int `url:",omitempty" json:",omitempty"`
	Page    int `url:",omitempty" json:",omitempty"`
}

const DefaultPerPage = 10

func (o ListOptions) PageOrDefault() int {
	if o.Page <= 0 {
		return 1
	}
	return o.Page
}

func (o ListOptions) PerPageOrDefault() int {
	if o.PerPage <= 0 {
		return DefaultPerPage
	}
	return o.PerPage
}

// Limit returns the number of items to fetch.
func (o ListOptions) Limit() int { return o.PerPageOrDefault() }

// Offset returns the 0-indexed offset of the first item that appears on this
// page, based on the PerPage and Page values (which are given default values if
// they are zero).
func (o ListOptions) Offset() int {
	return (o.PageOrDefault() - 1) * o.PerPageOrDefault()
}

type doKey int // sentinel value type for (*Client).Do v parameter

const preserveBody doKey = iota // when passed as v to (*Client).Do, the resp body is neither parsed nor closed

// Do sends an API request and returns the API response.  The API
// response is decoded and stored in the value pointed to by v, or
// returned as an error if an API error has occurred. If v is
// preserveBody, then the HTTP response body is not closed by Do; the
// caller is responsible for closing it.
func (c *Client) Do(req *http.Request, v interface{}) (*HTTPResponse, error) {
	var resp *HTTPResponse
	rawResp, err := c.httpClient.Do(req)
	if rawResp != nil {
		if v != preserveBody {
			defer rawResp.Body.Close()
		}
		resp = newResponse(rawResp)
		if err == nil {
			// Don't clobber error from Do, if any (it could be, e.g.,
			// a sentinel error returned by the HTTP client's
			// CheckRedirect func).
			if err := CheckResponse(rawResp); err != nil {
				// even though there was an error, we still return the response
				// in case the caller wants to inspect it further
				return resp, err
			}
		}
	}
	if err != nil {
		return resp, err
	}

	if v != nil {
		if bp, ok := v.(*[]byte); ok {
			*bp, err = ioutil.ReadAll(rawResp.Body)
		} else if v != preserveBody {
			err = json.NewDecoder(rawResp.Body).Decode(v)
		}
	}
	if err != nil {
		return resp, fmt.Errorf("error reading response from %s %s: %s", req.Method, req.URL.RequestURI(), err)
	}
	return resp, nil
}

// addOptions adds the parameters in opt as URL query parameters to u. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(u *url.URL, opt interface{}) error {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}

	qs, err := query.Values(opt)
	if err != nil {
		return err
	}

	u.RawQuery = qs.Encode()
	return nil
}

// NewMockClient returns a mockable Client for use in tests.
func NewMockClient() *Client {
	return &Client{
		BuildData:    &MockBuildDataService{},
		Builds:       &MockBuildsService{},
		Deltas:       &MockDeltasService{},
		Issues:       &MockIssuesService{},
		Orgs:         &MockOrgsService{},
		People:       &MockPeopleService{},
		PullRequests: &MockPullRequestsService{},
		Repos:        &MockReposService{},
		RepoTree:     &MockRepoTreeService{},
		Search:       &MockSearchService{},
		Units:        &MockUnitsService{},
		Users:        &MockUsersService{},
		Defs:         &MockDefsService{},
	}
}
<|endoftext|>


<commit_before>package presenter

import (
	"fmt"
	"strconv"

	"github.com/fatih/color"
)

type Float float64

func (f Float) String() string { return fmt.Sprintf("%.1f", f) }

type Int int

func (i Int) String() string { return strconv.Itoa(int(i)) }

type Injury string

func (injury Injury) String() string {
	i := string(injury)
	switch i {
	case "":
		return i
	case "Q":
		return color.YellowString(i)
	default:
		return color.RedString(i)
	}
}
<commit_msg>remove unused formatter<commit_after>package presenter

import (
	"fmt"
	"strconv"
)

type Float float64

func (f Float) String() string { return fmt.Sprintf("%.1f", f) }

type Int int

func (i Int) String() string { return strconv.Itoa(int(i)) }
<|endoftext|>


<commit_before>package proc

// #include "proc_darwin.h"
// #include "exec_darwin.h"
// #include <stdlib.h>
import "C"
import (
	"debug/gosym"
	"debug/macho"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"unsafe"

	"github.com/derekparker/delve/dwarf/frame"
	"github.com/derekparker/delve/dwarf/line"
	sys "golang.org/x/sys/unix"
)

// Darwin specific information.
type OSProcessDetails struct {
	task             C.mach_port_name_t // mach task for the debugged process.
	exceptionPort    C.mach_port_t      // mach port for receiving mach exceptions.
	notificationPort C.mach_port_t      // mach port for dead name notification (process exit).

	// the main port we use, will return messages from both the
	// exception and notification ports.
	portSet C.mach_port_t
}

// Create and begin debugging a new process. Uses a
// custom fork/exec process in order to take advantage of
// PT_SIGEXC on Darwin.
func Launch(cmd []string) (*DebuggedProcess, error) {
	argv0Go, err := filepath.Abs(cmd[0])
	if err != nil {
		return nil, err
	}
	argv0 := C.CString(argv0Go)

	argvSlice := make([]*C.char, 0, len(cmd))
	for _, arg := range cmd {
		argvSlice = append(argvSlice, C.CString(arg))
	}

	var argv **C.char
	argv = &argvSlice[0]

	dbp := New(0)
	var pid int
	dbp.execPtraceFunc(func() {
		ret := C.fork_exec(argv0, argv, &dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort, &dbp.os.notificationPort)
		pid = int(ret)
	})
	if pid <= 0 {
		return nil, fmt.Errorf("could not fork/exec")
	}
	dbp.Pid = pid
	for i := range argvSlice {
		C.free(unsafe.Pointer(argvSlice[i]))
	}

	dbp, err = initializeDebugProcess(dbp, argv0Go, false)
	if err != nil {
		return nil, err
	}
	err = dbp.Continue()
	return dbp, err
}

func (dbp *DebuggedProcess) requestManualStop() (err error) {
	var (
		task          = C.mach_port_t(dbp.os.task)
		thread        = C.mach_port_t(dbp.CurrentThread.os.thread_act)
		exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
	)
	kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
	if kret != C.KERN_SUCCESS {
		return fmt.Errorf("could not raise mach exception")
	}
	return nil
}

func (dbp *DebuggedProcess) updateThreadList() error {
	var (
		err   error
		kret  C.kern_return_t
		count = C.thread_count(C.task_t(dbp.os.task))
	)
	if count == -1 {
		return fmt.Errorf("could not get thread count")
	}
	list := make([]uint32, count)

	// TODO(dp) might be better to malloc mem in C and then free it here
	// instead of getting count above and passing in a slice
	kret = C.get_threads(C.task_t(dbp.os.task), unsafe.Pointer(&list[0]))
	if kret != C.KERN_SUCCESS {
		return fmt.Errorf("could not get thread list")
	}
	if count < 0 {
		return fmt.Errorf("could not get thread list")
	}

	for _, port := range list {
		if _, ok := dbp.Threads[int(port)]; !ok {
			_, err = dbp.addThread(int(port), false)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (dbp *DebuggedProcess) addThread(port int, attach bool) (*Thread, error) {
	if thread, ok := dbp.Threads[port]; ok {
		return thread, nil
	}
	thread := &Thread{
		Id:  port,
		dbp: dbp,
		os:  new(OSSpecificDetails),
	}
	dbp.Threads[port] = thread
	thread.os.thread_act = C.thread_act_t(port)
	if dbp.CurrentThread == nil {
		dbp.CurrentThread = thread
	}
	return thread, nil
}

func (dbp *DebuggedProcess) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
	defer wg.Done()

	if sec := exe.Section("__debug_frame"); sec != nil {
		debugFrame, err := exe.Section("__debug_frame").Data()
		if err != nil {
			fmt.Println("could not get __debug_frame section", err)
			os.Exit(1)
		}
		dbp.frameEntries = frame.Parse(debugFrame)
	} else {
		fmt.Println("could not find __debug_frame section in binary")
		os.Exit(1)
	}
}

func (dbp *DebuggedProcess) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
	defer wg.Done()

	var (
		symdat  []byte
		pclndat []byte
		err     error
	)

	if sec := exe.Section("__gosymtab"); sec != nil {
		symdat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gosymtab section", err)
			os.Exit(1)
		}
	}

	if sec := exe.Section("__gopclntab"); sec != nil {
		pclndat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gopclntab section", err)
			os.Exit(1)
		}
	}

	pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
	tab, err := gosym.NewTable(symdat, pcln)
	if err != nil {
		fmt.Println("could not get initialize line table", err)
		os.Exit(1)
	}

	dbp.goSymTable = tab
}

func (dbp *DebuggedProcess) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
	defer wg.Done()

	if sec := exe.Section("__debug_line"); sec != nil {
		debugLine, err := exe.Section("__debug_line").Data()
		if err != nil {
			fmt.Println("could not get __debug_line section", err)
			os.Exit(1)
		}
		dbp.lineInfo = line.Parse(debugLine)
	} else {
		fmt.Println("could not find __debug_line section in binary")
		os.Exit(1)
	}
}

func (dbp *DebuggedProcess) findExecutable(path string) (*macho.File, error) {
	if path == "" {
		path = C.GoString(C.find_executable(C.int(dbp.Pid)))
	}
	exe, err := macho.Open(path)
	if err != nil {
		return nil, err
	}
	data, err := exe.DWARF()
	if err != nil {
		return nil, err
	}
	dbp.dwarf = data
	return exe, nil
}

func (dbp *DebuggedProcess) trapWait(pid int) (*Thread, error) {
	var (
		th  *Thread
		err error
	)
	for {
		port := C.mach_port_wait(dbp.os.portSet)

		switch port {
		case dbp.os.notificationPort:
			_, status, err := wait(dbp.Pid, 0)
			if err != nil {
				return nil, err
			}
			dbp.exited = true
			return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
		case C.MACH_RCV_INTERRUPTED:
			if !dbp.halt {
				// Call trapWait again, it seems
				// MACH_RCV_INTERRUPTED is emitted before
				// process natural death _sometimes_.
				continue
			}
			return nil, ManualStopError{}
		case 0:
			return nil, fmt.Errorf("error while waiting for task")
		}

		// Since we cannot be notified of new threads on OS X
		// this is as good a time as any to check for them.
		dbp.updateThreadList()
		th, err = dbp.handleBreakpointOnThread(int(port))
		if err != nil {
			if _, ok := err.(NoBreakpointError); ok {
				th := dbp.Threads[int(port)]
				if dbp.firstStart || dbp.singleStepping || th.singleStepping {
					dbp.firstStart = false
					return dbp.Threads[int(port)], nil
				}
				if err := th.Continue(); err != nil {
					return nil, err
				}
				continue
			}
			return nil, err
		}
		return th, nil
	}
	return th, nil
}

func wait(pid, options int) (int, *sys.WaitStatus, error) {
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}
<commit_msg>Update Launch docs for Darwin<commit_after>package proc

// #include "proc_darwin.h"
// #include "exec_darwin.h"
// #include <stdlib.h>
import "C"
import (
	"debug/gosym"
	"debug/macho"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"unsafe"

	"github.com/derekparker/delve/dwarf/frame"
	"github.com/derekparker/delve/dwarf/line"
	sys "golang.org/x/sys/unix"
)

// Darwin specific information.
type OSProcessDetails struct {
	task             C.mach_port_name_t // mach task for the debugged process.
	exceptionPort    C.mach_port_t      // mach port for receiving mach exceptions.
	notificationPort C.mach_port_t      // mach port for dead name notification (process exit).

	// the main port we use, will return messages from both the
	// exception and notification ports.
	portSet C.mach_port_t
}

// Create and begin debugging a new process. Uses a
// custom fork/exec process in order to take advantage of
// PT_SIGEXC on Darwin which will turn Unix signals into
// Mach exceptions.
func Launch(cmd []string) (*DebuggedProcess, error) {
	argv0Go, err := filepath.Abs(cmd[0])
	if err != nil {
		return nil, err
	}
	argv0 := C.CString(argv0Go)

	argvSlice := make([]*C.char, 0, len(cmd))
	for _, arg := range cmd {
		argvSlice = append(argvSlice, C.CString(arg))
	}

	var argv **C.char
	argv = &argvSlice[0]

	dbp := New(0)
	var pid int
	dbp.execPtraceFunc(func() {
		ret := C.fork_exec(argv0, argv, &dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort, &dbp.os.notificationPort)
		pid = int(ret)
	})
	if pid <= 0 {
		return nil, fmt.Errorf("could not fork/exec")
	}
	dbp.Pid = pid
	for i := range argvSlice {
		C.free(unsafe.Pointer(argvSlice[i]))
	}

	dbp, err = initializeDebugProcess(dbp, argv0Go, false)
	if err != nil {
		return nil, err
	}
	err = dbp.Continue()
	return dbp, err
}

func (dbp *DebuggedProcess) requestManualStop() (err error) {
	var (
		task          = C.mach_port_t(dbp.os.task)
		thread        = C.mach_port_t(dbp.CurrentThread.os.thread_act)
		exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
	)
	kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
	if kret != C.KERN_SUCCESS {
		return fmt.Errorf("could not raise mach exception")
	}
	return nil
}

func (dbp *DebuggedProcess) updateThreadList() error {
	var (
		err   error
		kret  C.kern_return_t
		count = C.thread_count(C.task_t(dbp.os.task))
	)
	if count == -1 {
		return fmt.Errorf("could not get thread count")
	}
	list := make([]uint32, count)

	// TODO(dp) might be better to malloc mem in C and then free it here
	// instead of getting count above and passing in a slice
	kret = C.get_threads(C.task_t(dbp.os.task), unsafe.Pointer(&list[0]))
	if kret != C.KERN_SUCCESS {
		return fmt.Errorf("could not get thread list")
	}
	if count < 0 {
		return fmt.Errorf("could not get thread list")
	}

	for _, port := range list {
		if _, ok := dbp.Threads[int(port)]; !ok {
			_, err = dbp.addThread(int(port), false)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (dbp *DebuggedProcess) addThread(port int, attach bool) (*Thread, error) {
	if thread, ok := dbp.Threads[port]; ok {
		return thread, nil
	}
	thread := &Thread{
		Id:  port,
		dbp: dbp,
		os:  new(OSSpecificDetails),
	}
	dbp.Threads[port] = thread
	thread.os.thread_act = C.thread_act_t(port)
	if dbp.CurrentThread == nil {
		dbp.CurrentThread = thread
	}
	return thread, nil
}

func (dbp *DebuggedProcess) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
	defer wg.Done()

	if sec := exe.Section("__debug_frame"); sec != nil {
		debugFrame, err := exe.Section("__debug_frame").Data()
		if err != nil {
			fmt.Println("could not get __debug_frame section", err)
			os.Exit(1)
		}
		dbp.frameEntries = frame.Parse(debugFrame)
	} else {
		fmt.Println("could not find __debug_frame section in binary")
		os.Exit(1)
	}
}

func (dbp *DebuggedProcess) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
	defer wg.Done()

	var (
		symdat  []byte
		pclndat []byte
		err     error
	)

	if sec := exe.Section("__gosymtab"); sec != nil {
		symdat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gosymtab section", err)
			os.Exit(1)
		}
	}

	if sec := exe.Section("__gopclntab"); sec != nil {
		pclndat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gopclntab section", err)
			os.Exit(1)
		}
	}

	pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
	tab, err := gosym.NewTable(symdat, pcln)
	if err != nil {
		fmt.Println("could not get initialize line table", err)
		os.Exit(1)
	}

	dbp.goSymTable = tab
}

func (dbp *DebuggedProcess) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
	defer wg.Done()

	if sec := exe.Section("__debug_line"); sec != nil {
		debugLine, err := exe.Section("__debug_line").Data()
		if err != nil {
			fmt.Println("could not get __debug_line section", err)
			os.Exit(1)
		}
		dbp.lineInfo = line.Parse(debugLine)
	} else {
		fmt.Println("could not find __debug_line section in binary")
		os.Exit(1)
	}
}

func (dbp *DebuggedProcess) findExecutable(path string) (*macho.File, error) {
	if path == "" {
		path = C.GoString(C.find_executable(C.int(dbp.Pid)))
	}
	exe, err := macho.Open(path)
	if err != nil {
		return nil, err
	}
	data, err := exe.DWARF()
	if err != nil {
		return nil, err
	}
	dbp.dwarf = data
	return exe, nil
}

func (dbp *DebuggedProcess) trapWait(pid int) (*Thread, error) {
	var (
		th  *Thread
		err error
	)
	for {
		port := C.mach_port_wait(dbp.os.portSet)

		switch port {
		case dbp.os.notificationPort:
			_, status, err := wait(dbp.Pid, 0)
			if err != nil {
				return nil, err
			}
			dbp.exited = true
			return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
		case C.MACH_RCV_INTERRUPTED:
			if !dbp.halt {
				// Call trapWait again, it seems
				// MACH_RCV_INTERRUPTED is emitted before
				// process natural death _sometimes_.
				continue
			}
			return nil, ManualStopError{}
		case 0:
			return nil, fmt.Errorf("error while waiting for task")
		}

		// Since we cannot be notified of new threads on OS X
		// this is as good a time as any to check for them.
		dbp.updateThreadList()
		th, err = dbp.handleBreakpointOnThread(int(port))
		if err != nil {
			if _, ok := err.(NoBreakpointError); ok {
				th := dbp.Threads[int(port)]
				if dbp.firstStart || dbp.singleStepping || th.singleStepping {
					dbp.firstStart = false
					return dbp.Threads[int(port)], nil
				}
				if err := th.Continue(); err != nil {
					return nil, err
				}
				continue
			}
			return nil, err
		}
		return th, nil
	}
	return th, nil
}

func wait(pid, options int) (int, *sys.WaitStatus, error) {
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}
<|endoftext|>


<commit_before>package tests

import (
	"fmt"
	"reflect"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"

	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	cdiClientset "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
	"kubevirt.io/containerized-data-importer/tests/framework"
	"kubevirt.io/containerized-data-importer/tests/utils"
)

var _ = Describe("Aggregated role in-action tests", func() {
	var createServiceAccount = func(client kubernetes.Interface, namespace, name string) {
		sa := &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{
				Name: name,
			},
		}

		_, err := client.CoreV1().ServiceAccounts(namespace).Create(sa)
		Expect(err).ToNot(HaveOccurred())
	}

	var createRoleBinding = func(client kubernetes.Interface, clusterRoleName, namespace, serviceAccount string) {
		rb := &rbacv1.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				Name: serviceAccount,
			},
			RoleRef: rbacv1.RoleRef{
				Kind:     "ClusterRole",
				Name:     clusterRoleName,
				APIGroup: "rbac.authorization.k8s.io",
			},
			Subjects: []rbacv1.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      serviceAccount,
					Namespace: namespace,
				},
			},
		}

		_, err := client.RbacV1().RoleBindings(namespace).Create(rb)
		Expect(err).ToNot(HaveOccurred())
	}

	f := framework.NewFrameworkOrDie("aggregated-role-tests")

	DescribeTable("admin/edit datavolume permission checks", func(user string) {
		var client cdiClientset.Interface
		var err error

		createServiceAccount(f.K8sClient, f.Namespace.Name, user)
		createRoleBinding(f.K8sClient, user, f.Namespace.Name, user)

		Eventually(func() error {
			client, err = f.GetCdiClientForServiceAccount(f.Namespace.Name, user)
			return err
		}, 60*time.Second, 2*time.Second).ShouldNot(HaveOccurred())

		dv := utils.NewDataVolumeWithHTTPImport("test-"+user, "1Gi", "http://nonexistant.url")
		dv, err = client.Cdi().DataVolumes(f.Namespace.Name).Create(dv)
		Expect(err).ToNot(HaveOccurred())

		dvl, err := client.Cdi().DataVolumes(f.Namespace.Name).List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(dvl.Items).To(HaveLen(1))

		dv, err = client.Cdi().DataVolumes(f.Namespace.Name).Get(dv.Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		err = client.Cdi().DataVolumes(f.Namespace.Name).Delete(dv.Name, &metav1.DeleteOptions{})
		Expect(err).ToNot(HaveOccurred())

		dvl, err = client.Cdi().DataVolumes(f.Namespace.Name).List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(dvl.Items).To(HaveLen(0))

		cl, err := client.Cdi().CDIConfigs().List(metav1.ListOptions{})
		fmt.Printf("XXX %+v\n", err)
		Expect(err).ToNot(HaveOccurred())
		Expect(cl.Items).To(HaveLen(1))

		cfg, err := client.Cdi().CDIConfigs().Get(cl.Items[0].Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		cfg.Spec.ScratchSpaceStorageClass = &[]string{"foobar"}[0]
		cfg, err = client.Cdi().CDIConfigs().Update(cfg)
		Expect(err).To(HaveOccurred())
	},
		Entry("can do everything with admin", "admin"),
		Entry("can do everything with edit", "edit"),
	)

	It("view datavolume permission checks", func() {
		const user = "view"
		var client cdiClientset.Interface
		var err error

		createServiceAccount(f.K8sClient, f.Namespace.Name, user)
		createRoleBinding(f.K8sClient, user, f.Namespace.Name, user)

		Eventually(func() error {
			client, err = f.GetCdiClientForServiceAccount(f.Namespace.Name, user)
			return err
		}, 60*time.Second, 2*time.Second).ShouldNot(HaveOccurred())

		dv := utils.NewDataVolumeWithHTTPImport("test-"+user, "1Gi", "http://nonexistant.url")
		dv, err = client.Cdi().DataVolumes(f.Namespace.Name).Create(dv)
		Expect(err).To(HaveOccurred())

		dvl, err := client.Cdi().DataVolumes(f.Namespace.Name).List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(dvl.Items).To(HaveLen(0))

		_, err = client.Cdi().DataVolumes(f.Namespace.Name).Get("test-"+user, metav1.GetOptions{})
		Expect(err).To(HaveOccurred())
		Expect(k8serrors.IsNotFound(err)).To(BeTrue())

		cl, err := client.Cdi().CDIConfigs().List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(cl.Items).To(HaveLen(1))

		cfg, err := client.Cdi().CDIConfigs().Get(cl.Items[0].Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		cfg.Spec.ScratchSpaceStorageClass = &[]string{"foobar"}[0]
		cfg, err = client.Cdi().CDIConfigs().Update(cfg)
		Expect(err).To(HaveOccurred())
	})
})

var _ = Describe("Aggregated role definition tests", func() {
	var adminRules = []rbacv1.PolicyRule{
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes",
			},
			Verbs: []string{
				"*",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes/source",
			},
			Verbs: []string{
				"create",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"cdiconfigs",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"patch",
				"update",
			},
		},
		{
			APIGroups: []string{
				"upload.cdi.kubevirt.io",
			},
			Resources: []string{
				"uploadtokenrequests",
			},
			Verbs: []string{
				"*",
			},
		},
	}

	var editRules = adminRules

	var viewRules = []rbacv1.PolicyRule{
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes/source",
			},
			Verbs: []string{
				"create",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"cdiconfigs",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
			},
		},
	}

	f := framework.NewFrameworkOrDie("aggregated-role-definition-tests")

	DescribeTable("check all expected rules exist", func(role string, rules []rbacv1.PolicyRule) {
		clusterRole, err := f.K8sClient.RbacV1().ClusterRoles().Get(role, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		found := false
		for _, expectedRule := range rules {
			for _, r := range clusterRole.Rules {
				if reflect.DeepEqual(expectedRule, r) {
					found = true
					break
				}
			}
		}
		Expect(found).To(BeTrue())
	},
		Entry("for admin", "admin", adminRules),
		Entry("for edit", "edit", editRules),
		Entry("for view", "view", viewRules),
	)
})
<commit_msg>additional RBAC test (#983)<commit_after>package tests

import (
	"reflect"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"

	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	cdiClientset "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
	"kubevirt.io/containerized-data-importer/tests/framework"
	"kubevirt.io/containerized-data-importer/tests/utils"
)

var _ = Describe("Aggregated role in-action tests", func() {
	var createServiceAccount = func(client kubernetes.Interface, namespace, name string) {
		sa := &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{
				Name: name,
			},
		}

		_, err := client.CoreV1().ServiceAccounts(namespace).Create(sa)
		Expect(err).ToNot(HaveOccurred())
	}

	var createRoleBinding = func(client kubernetes.Interface, clusterRoleName, namespace, serviceAccount string) {
		rb := &rbacv1.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				Name: serviceAccount,
			},
			RoleRef: rbacv1.RoleRef{
				Kind:     "ClusterRole",
				Name:     clusterRoleName,
				APIGroup: "rbac.authorization.k8s.io",
			},
			Subjects: []rbacv1.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      serviceAccount,
					Namespace: namespace,
				},
			},
		}

		_, err := client.RbacV1().RoleBindings(namespace).Create(rb)
		Expect(err).ToNot(HaveOccurred())
	}

	f := framework.NewFrameworkOrDie("aggregated-role-tests")

	DescribeTable("admin/edit datavolume permission checks", func(user string) {
		var client *cdiClientset.Clientset
		var err error

		createServiceAccount(f.K8sClient, f.Namespace.Name, user)
		createRoleBinding(f.K8sClient, user, f.Namespace.Name, user)

		Eventually(func() error {
			client, err = f.GetCdiClientForServiceAccount(f.Namespace.Name, user)
			return err
		}, 60*time.Second, 2*time.Second).ShouldNot(HaveOccurred())

		dv := utils.NewDataVolumeWithHTTPImport("test-"+user, "1Gi", "http://nonexistant.url")
		dv, err = client.Cdi().DataVolumes(f.Namespace.Name).Create(dv)
		Expect(err).ToNot(HaveOccurred())

		dvl, err := client.Cdi().DataVolumes(f.Namespace.Name).List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(dvl.Items).To(HaveLen(1))

		dv, err = client.Cdi().DataVolumes(f.Namespace.Name).Get(dv.Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		err = client.Cdi().DataVolumes(f.Namespace.Name).Delete(dv.Name, &metav1.DeleteOptions{})
		Expect(err).ToNot(HaveOccurred())

		dvl, err = client.Cdi().DataVolumes(f.Namespace.Name).List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(dvl.Items).To(HaveLen(0))

		dv = utils.NewDataVolumeForUpload("upload-test-"+user, "1Gi")
		dv, err = client.Cdi().DataVolumes(f.Namespace.Name).Create(dv)
		Expect(err).ToNot(HaveOccurred())

		var pvc *corev1.PersistentVolumeClaim
		Eventually(func() error {
			pvc, err = f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(dv.Name, metav1.GetOptions{})
			if err != nil {
				return err
			}
			return nil
		}, 90*time.Second, 2*time.Second).ShouldNot(HaveOccurred())

		found, err := utils.WaitPVCPodStatusRunning(f.K8sClient, pvc)
		Expect(err).ToNot(HaveOccurred())
		Expect(found).Should(BeTrue())

		token, err := utils.RequestUploadToken(client, pvc)
		Expect(err).ToNot(HaveOccurred())
		Expect(token).ToNot(BeEmpty())

		cl, err := client.Cdi().CDIConfigs().List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(cl.Items).To(HaveLen(1))

		cfg, err := client.Cdi().CDIConfigs().Get(cl.Items[0].Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		cfg.Spec.ScratchSpaceStorageClass = &[]string{"foobar"}[0]
		cfg, err = client.Cdi().CDIConfigs().Update(cfg)
		Expect(err).To(HaveOccurred())
	},
		Entry("can do everything with admin", "admin"),
		Entry("can do everything with edit", "edit"),
	)

	It("view datavolume permission checks", func() {
		const user = "view"
		var client cdiClientset.Interface
		var err error

		createServiceAccount(f.K8sClient, f.Namespace.Name, user)
		createRoleBinding(f.K8sClient, user, f.Namespace.Name, user)

		Eventually(func() error {
			client, err = f.GetCdiClientForServiceAccount(f.Namespace.Name, user)
			return err
		}, 60*time.Second, 2*time.Second).ShouldNot(HaveOccurred())

		dv := utils.NewDataVolumeWithHTTPImport("test-"+user, "1Gi", "http://nonexistant.url")
		dv, err = client.Cdi().DataVolumes(f.Namespace.Name).Create(dv)
		Expect(err).To(HaveOccurred())

		dvl, err := client.Cdi().DataVolumes(f.Namespace.Name).List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(dvl.Items).To(HaveLen(0))

		_, err = client.Cdi().DataVolumes(f.Namespace.Name).Get("test-"+user, metav1.GetOptions{})
		Expect(err).To(HaveOccurred())
		Expect(k8serrors.IsNotFound(err)).To(BeTrue())

		cl, err := client.Cdi().CDIConfigs().List(metav1.ListOptions{})
		Expect(err).ToNot(HaveOccurred())
		Expect(cl.Items).To(HaveLen(1))

		cfg, err := client.Cdi().CDIConfigs().Get(cl.Items[0].Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		cfg.Spec.ScratchSpaceStorageClass = &[]string{"foobar"}[0]
		cfg, err = client.Cdi().CDIConfigs().Update(cfg)
		Expect(err).To(HaveOccurred())
	})
})

var _ = Describe("Aggregated role definition tests", func() {
	var adminRules = []rbacv1.PolicyRule{
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes",
			},
			Verbs: []string{
				"*",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes/source",
			},
			Verbs: []string{
				"create",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"cdiconfigs",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"patch",
				"update",
			},
		},
		{
			APIGroups: []string{
				"upload.cdi.kubevirt.io",
			},
			Resources: []string{
				"uploadtokenrequests",
			},
			Verbs: []string{
				"*",
			},
		},
	}

	var editRules = adminRules

	var viewRules = []rbacv1.PolicyRule{
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"datavolumes/source",
			},
			Verbs: []string{
				"create",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"cdiconfigs",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
			},
		},
	}

	f := framework.NewFrameworkOrDie("aggregated-role-definition-tests")

	DescribeTable("check all expected rules exist", func(role string, rules []rbacv1.PolicyRule) {
		clusterRole, err := f.K8sClient.RbacV1().ClusterRoles().Get(role, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())

		found := false
		for _, expectedRule := range rules {
			for _, r := range clusterRole.Rules {
				if reflect.DeepEqual(expectedRule, r) {
					found = true
					break
				}
			}
		}
		Expect(found).To(BeTrue())
	},
		Entry("for admin", "admin", adminRules),
		Entry("for edit", "edit", editRules),
		Entry("for view", "view", viewRules),
	)
})
<|endoftext|>


<commit_before>package contractor

import (
	"fmt"
	"reflect"
	"regexp"
	"strings"
)

func NewContractorCase(i interface{}) ContractorCase {

	return ContractorCase{i}
}

type ContractorCase struct {
	Case interface{}
}

// Set the values for the current Case
func (C *ContractorCase) Set(fields map[string]interface{}) {
	if len(fields) > 0 {
		t := reflect.TypeOf(C.Case)

		if t.Kind() == reflect.Ptr {
			t = t.Elem()
		} else {
			fmt.Printf("Contractor: Case must be a pointer, but got: %t", C.Case)
		}

		if t.Kind() == reflect.Struct {
			dest := reflect.ValueOf(C.Case)

			for field, val := range fields {
				var destTempField reflect.Value

				// check if field contains a dot.
				matchDot, _ := regexp.MatchString("\\.", field)
				if matchDot == true {
					parts := strings.Split(field, ".")

					// For now only 1 sublevel.. gotta make this recursive...
					if len(parts) == 2 {
						destTempField = dest.Elem().FieldByName(parts[0])

						if destTempField.IsValid() {
							if destTempField.Type().Kind() == reflect.Struct {
								sublevel := destTempField.FieldByName(parts[1])

								if sublevel.IsValid() {
									if sublevel.CanSet() {
										destTempField = sublevel
									} else {
										continue
									}
								} else {
									continue
								}
							} else {
								continue
							}
						} else {
							continue
						}
					} else {
						continue
					}
				} else {
					destTempField = dest.Elem().FieldByName(field)
				}

				destField := destTempField
				rval := reflect.ValueOf(val)

				if destField.IsValid() {
					if destField.CanSet() {

						switch destField.Type().Kind() {
						case reflect.String:
							destField.SetString(rval.String())

						case reflect.Bool:
							destField.Set(reflect.ValueOf(val))

						case reflect.Int, reflect.Int32, reflect.Int64:

							destField.SetInt(reflect.ValueOf(val).Int())

						case reflect.Float32, reflect.Float64:
							destField.Set(reflect.ValueOf(val))

						case reflect.Slice:

							switch reflect.ValueOf(val).Type().Kind() {
							case reflect.Slice:
								CurVal := reflect.ValueOf(val)

								if CurVal.CanInterface() {
									CurLen := CurVal.Len()

									if CurLen > 0 {
										for i := 0; i < CurLen; i++ {
											// Since it is an pointer, get the Inderect value.
											CurSlice := reflect.Indirect(reflect.ValueOf(CurVal.Index(i).Interface()))

											destField.Set(reflect.Append(destField, CurSlice))
										}
									}
								}
							}
						case reflect.Struct, reflect.Ptr:

							switch reflect.ValueOf(val).Type().Kind() {
							case reflect.Struct:
								destField.Set(reflect.ValueOf(val))
							case reflect.Ptr:
								destField.Set(reflect.ValueOf(val))
							}
						default:
							fmt.Printf("Contractor: unknown kind to set: ` %v ` . please file a request to http://github.com/donseba/contractor \n", destField.Type().Kind())
						}
					}
				}
			}
		}
	}
}

func (C *ContractorCase) Get() interface{} {
	return reflect.ValueOf(C.Case).Interface()
}

func (C *ContractorCase) CaseItem(field string) interface{} {
	t := reflect.TypeOf(C.Case)

	if t.Kind() != reflect.Ptr {
		fmt.Printf("Contractor: `CaseItem` Case must be a pointer, but got: %t", C.Case)
	}

	dest := reflect.ValueOf(C.Case)
	destField := dest.Elem().FieldByName(field)

	return destField.Interface()

}
<commit_msg>Adding possibility to traverse into structs without limit...<commit_after>package contractor

import (
	"fmt"
	"reflect"
	"regexp"
	"strings"
)

//
func NewContractorCase(i interface{}) ContractorCase {
	return ContractorCase{i}
}

//
type ContractorCase struct {
	Case interface{}
}

// Set the values for the current Case
func (C *ContractorCase) Set(fields map[string]interface{}) {
	if len(fields) > 0 {
		t := reflect.TypeOf(C.Case)

		if t.Kind() == reflect.Ptr {
			t = t.Elem()
		} else {
			fmt.Printf("Contractor: Case must be a pointer, but got: %t", C.Case)
		}

		if t.Kind() == reflect.Struct {
			dest := reflect.ValueOf(C.Case)

			for field, val := range fields {
				var destTempField reflect.Value

				// check if field contains a dot.
				matchDot, _ := regexp.MatchString("\\.", field)
				if matchDot == true {
					destTempField = C.getNestedField(dest, field)
				} else {
					destTempField = dest.Elem().FieldByName(field)
				}

				destField := destTempField
				rval := reflect.ValueOf(val)

				if destField.IsValid() {
					if destField.CanSet() {

						switch destField.Type().Kind() {
						case reflect.String:
							destField.SetString(rval.String())

						case reflect.Bool:
							destField.Set(reflect.ValueOf(val))

						case reflect.Int, reflect.Int32, reflect.Int64:

							destField.SetInt(reflect.ValueOf(val).Int())

						case reflect.Float32, reflect.Float64:
							destField.Set(reflect.ValueOf(val))

						case reflect.Slice:

							switch reflect.ValueOf(val).Type().Kind() {
							case reflect.Slice:
								CurVal := reflect.ValueOf(val)

								if CurVal.CanInterface() {
									CurLen := CurVal.Len()

									if CurLen > 0 {
										for i := 0; i < CurLen; i++ {
											// Since it is an pointer, get the Inderect value.
											CurSlice := reflect.Indirect(reflect.ValueOf(CurVal.Index(i).Interface()))

											destField.Set(reflect.Append(destField, CurSlice))
										}
									}
								}
							}
						case reflect.Struct, reflect.Ptr:

							switch reflect.ValueOf(val).Type().Kind() {
							case reflect.Struct:
								destField.Set(reflect.ValueOf(val))
							case reflect.Ptr:
								destField.Set(reflect.ValueOf(val))
							}
						default:
							fmt.Printf("Contractor: unknown kind to set: ` %v ` . please file a request to http://github.com/donseba/contractor \n", destField.Type().Kind())
						}
					}
				}
			}
		}
	}
}

//
func (C *ContractorCase) Get() interface{} {
	return reflect.ValueOf(C.Case).Interface()
}

// Get a specific item inside a case.
func (C *ContractorCase) Item(field string) interface{} {
	t := reflect.TypeOf(C.Case)

	if t.Kind() != reflect.Ptr {
		fmt.Printf("Contractor: `CaseItem` Case must be a pointer, but got: %t", C.Case)
	}

	dest := reflect.ValueOf(C.Case)
	destField := dest.Elem().FieldByName(field)

	return destField.Interface()
}

// Try to reach the nested struct item value.
func (C *ContractorCase) getNestedField(dest reflect.Value, field string) reflect.Value {
	parts := strings.Split(field, ".")

	destTempField := dest.Elem().FieldByName(parts[0])

	for i := 1; i < len(parts); i++ {
		if destTempField.IsValid() {
			if destTempField.Type().Kind() == reflect.Struct {
				sublevel := destTempField.FieldByName(parts[i])

				if sublevel.IsValid() {
					if sublevel.CanSet() {
						destTempField = sublevel
					}
				}
			}
		}
	}

	return destTempField
}
<|endoftext|>


<commit_before>package gcs

import (
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	storage "google.golang.org/api/storage/v1"
	"log"
)

const (
	scope = storage.DevstorageReadOnlyScope
)

var (
	client   *context.Context
	service  *storage.Service
	oService *storage.ObjectsService
)

func InitConfig() {

	client, err := google.DefaultClient(context.Background(), scope)
	if err != nil {
		log.Fatalf("Unable to get default client: %v", err)
	}

	service, err = storage.New(client)
	if err != nil {
		log.Fatalf("Unable to create storage service: %v", err)
	}

	oService = storage.NewObjectsService(service)
	if err != nil {
		log.Fatalf("Unable to create objects storage service: %v", err)
	}

}
<commit_msg>explicit auth: add 2 additional auth methods<commit_after>package gcs

import (
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	storage "google.golang.org/api/storage/v1"
)

const (
	scope                  = storage.DevstorageReadOnlyScope
	accessTokenPath        = "/etc/apt/gcs_access_token"
	serviceAccountJSONPath = "/etc/apt/gcs_sa_json"
)

var (
	client   *context.Context
	service  *storage.Service
	oService *storage.ObjectsService
)

var ctx context.Context = context.Background()

// InitConfig creates the google storage client that is used from the apt package
func InitConfig() {
	client, err := getClient()
	if err != nil {
		log.Fatalf("Unable to get client: %v", err)
	}
	service, err = storage.New(client)
	if err != nil {
		log.Fatalf("Unable to create storage service: %v", err)
	}

	oService = storage.NewObjectsService(service)
	if err != nil {
		log.Fatalf("Unable to create objects storage service: %v", err)
	}

}

// getClient returns an authenticated http client based on a different set of GCP
// auth methods, cascading in the following order:
// if access_token (bearer) is present in /etc/apt/gcs_access_token use it,
// else if Service Account JSON key is present in /etc/apt/gcs_sa_json use it,
// else try to get Application Default credentials https://github.com/golang/oauth2/blob/master/google/default.go

func getClient() (client *http.Client, err error) {
	switch {
	case fileExists(accessTokenPath):
		client, err = clientFromAccessToken(accessTokenPath)
		if err != nil {
			log.Fatalf("Unable to get client: %v", err)
		}
	case fileExists(serviceAccountJSONPath):
		client, err = clientFromServiceAccount(serviceAccountJSONPath)
		if err != nil {
			log.Fatalf("Unable to get client: %v", err)
		}
	default:
		client, err = google.DefaultClient(ctx, scope)
		if err != nil {
			log.Fatalf("Unable to get client: %v", err)
		}
	}
	return client, err
}

// clientFromAccessToken creates an http client authenticated using a GCS access_token (gcloud auth print-access-token)
func clientFromAccessToken(accessTokenPath string) (client *http.Client, err error) {
	tokenBytes, err := ioutil.ReadFile(accessTokenPath)
	if err != nil {
		log.Fatalf("Error while reading access_token file: %v", err)
	}
	token := oauth2.Token{
		AccessToken: string(tokenBytes),
	}
	tokenSource := oauth2.StaticTokenSource(&token)
	return oauth2.NewClient(ctx, tokenSource), err
}

// clientFromServiceAccount creates an http client authenticated using a GCS Service account JSON key
func clientFromServiceAccount(serviceAccountJSONPath string) (client *http.Client, err error) {
	JSONBytes, err := ioutil.ReadFile(serviceAccountJSONPath)
	if err != nil {
		log.Fatalf("Error while reading SA json file: %v", err)
	}
	credentials, err := google.CredentialsFromJSON(ctx, JSONBytes, scope)
	tokenSource := credentials.TokenSource
	return oauth2.NewClient(ctx, tokenSource), err
}

// fileExists checks if a file exists
func fileExists(filename string) bool {
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return false
	}
	return !info.IsDir()
}
<|endoftext|>