<commit_before>package graylog // import "gopkg.in/gemnasium/logrus-graylog-hook.v1"

import (
	"bytes"
	"fmt"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/SocialCodeInc/go-gelf/gelf"
)

// Set graylog.BufSize = <value> _before_ calling NewGraylogHook
// Once the buffer is full, logging will start blocking, waiting for slots to
// be available in the queue.
var BufSize uint = 8192

// GraylogHook to send logs to a logging service compatible with the Graylog API and the GELF format.
type GraylogHook struct {
	Facility   string
	Extra      map[string]interface{}
	gelfLogger *gelf.Writer
	buf        chan graylogEntry
}

// Graylog needs file and line params
type graylogEntry struct {
	*logrus.Entry
	file string
	line int
}

// NewGraylogHook creates a hook to be added to an instance of logger.
func NewGraylogHook(addr string, facility string, extra map[string]interface{}) *GraylogHook {
	g, err := gelf.NewWriter(addr)
	if err != nil {
		logrus.WithField("err", err).Info("Can't create Gelf logger")
	}
	hook := &GraylogHook{
		Facility:   facility,
		Extra:      extra,
		gelfLogger: g,
		buf:        make(chan graylogEntry, BufSize),
	}
	go hook.fire() // Log in background
	return hook
}

// Fire is called when a log event is fired.
// We assume the entry will be altered by another hook,
// otherwise we might logging something wrong to Graylog
func (hook *GraylogHook) Fire(entry *logrus.Entry) error {
	// get caller file and line here, it won't be available inside the goroutine
	// 1 for the function that called us.
	file, line := getCallerIgnoringLogMulti(1)

	gEntry := graylogEntry{entry, file, line}
	// Don't exit before sending the message
	if entry.Level == logrus.FatalLevel || entry.Level == logrus.PanicLevel {
		hook.sendEntry(gEntry)
		return nil

	}
	hook.buf <- gEntry
	return nil
}

// fire will loop on the 'buf' channel, and write entries to graylog
func (hook *GraylogHook) fire() {
	for {
		entry := <-hook.buf // receive new entry on channel
		hook.sendEntry(entry)
	}
}

// sendEntry sends an entry to graylog synchronously
func (hook *GraylogHook) sendEntry(entry graylogEntry) {
	host, err := os.Hostname()
	if err != nil {
		host = "localhost"
	}

	w := hook.gelfLogger

	// remove trailing and leading whitespace
	p := bytes.TrimSpace([]byte(entry.Message))

	// If there are newlines in the message, use the first line
	// for the short message and set the full message to the
	// original input.  If the input has no newlines, stick the
	// whole thing in Short.
	short := p
	full := []byte("")
	if i := bytes.IndexRune(p, '\n'); i > 0 {
		short = p[:i]
		full = p
	}

	level := int32(entry.Level) + 2 // logrus levels are lower than syslog by 2

	// Don't modify entry.Data directly, as the entry will used after this hook was fired
	extra := map[string]interface{}{}
	// Merge extra fields
	for k, v := range hook.Extra {
		k = fmt.Sprintf("_%s", k) // "[...] every field you send and prefix with a _ (underscore) will be treated as an additional field."
		extra[k] = v
	}
	for k, v := range entry.Data {
		k = fmt.Sprintf("_%s", k) // "[...] every field you send and prefix with a _ (underscore) will be treated as an additional field."
		extra[k] = v
	}

	m := gelf.Message{
		Version:  "1.1",
		Host:     host,
		Short:    string(short),
		Full:     string(full),
		TimeUnix: time.Now().Unix(),
		Level:    level,
		Facility: hook.Facility,
		File:     entry.file,
		Line:     entry.line,
		Extra:    extra,
	}

	if err := w.WriteMessage(&m); err != nil {
		fmt.Println(err)
	}

}

// Levels returns the available logging levels.
func (hook *GraylogHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
		logrus.WarnLevel,
		logrus.InfoLevel,
		logrus.DebugLevel,
	}
}

// getCaller returns the filename and the line info of a function
// further down in the call stack.  Passing 0 in as callDepth would
// return info on the function calling getCallerIgnoringLog, 1 the
// parent function, and so on.  Any suffixes passed to getCaller are
// path fragments like "/pkg/log/log.go", and functions in the call
// stack from that file are ignored.
func getCaller(callDepth int, suffixesToIgnore ...string) (file string, line int) {
	// bump by 1 to ignore the getCaller (this) stackframe
	callDepth++
outer:
	for {
		var ok bool
		_, file, line, ok = runtime.Caller(callDepth)
		if !ok {
			file = "???"
			line = 0
			break
		}

		for _, s := range suffixesToIgnore {
			if strings.HasSuffix(file, s) {
				callDepth++
				continue outer
			}
		}
		break
	}
	return
}

func getCallerIgnoringLogMulti(callDepth int) (string, int) {
	// the +1 is to ignore this (getCallerIgnoringLogMulti) frame
	return getCaller(callDepth+1, "logrus/hooks.go", "logrus/entry.go", "logrus/logger.go", "logrus/exported.go", "asm_amd64.s")
}
<commit_msg>Log in nano second instead<commit_after>package graylog // import "gopkg.in/gemnasium/logrus-graylog-hook.v1"

import (
	"bytes"
	"fmt"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/SocialCodeInc/go-gelf/gelf"
)

// Set graylog.BufSize = <value> _before_ calling NewGraylogHook
// Once the buffer is full, logging will start blocking, waiting for slots to
// be available in the queue.
var BufSize uint = 8192

// GraylogHook to send logs to a logging service compatible with the Graylog API and the GELF format.
type GraylogHook struct {
	Facility   string
	Extra      map[string]interface{}
	gelfLogger *gelf.Writer
	buf        chan graylogEntry
}

// Graylog needs file and line params
type graylogEntry struct {
	*logrus.Entry
	file string
	line int
}

// NewGraylogHook creates a hook to be added to an instance of logger.
func NewGraylogHook(addr string, facility string, extra map[string]interface{}) *GraylogHook {
	g, err := gelf.NewWriter(addr)
	if err != nil {
		logrus.WithField("err", err).Info("Can't create Gelf logger")
	}
	hook := &GraylogHook{
		Facility:   facility,
		Extra:      extra,
		gelfLogger: g,
		buf:        make(chan graylogEntry, BufSize),
	}
	go hook.fire() // Log in background
	return hook
}

// Fire is called when a log event is fired.
// We assume the entry will be altered by another hook,
// otherwise we might logging something wrong to Graylog
func (hook *GraylogHook) Fire(entry *logrus.Entry) error {
	// get caller file and line here, it won't be available inside the goroutine
	// 1 for the function that called us.
	file, line := getCallerIgnoringLogMulti(1)

	gEntry := graylogEntry{entry, file, line}
	// Don't exit before sending the message
	if entry.Level == logrus.FatalLevel || entry.Level == logrus.PanicLevel {
		hook.sendEntry(gEntry)
		return nil

	}
	hook.buf <- gEntry
	return nil
}

// fire will loop on the 'buf' channel, and write entries to graylog
func (hook *GraylogHook) fire() {
	for {
		entry := <-hook.buf // receive new entry on channel
		hook.sendEntry(entry)
	}
}

// sendEntry sends an entry to graylog synchronously
func (hook *GraylogHook) sendEntry(entry graylogEntry) {
	host, err := os.Hostname()
	if err != nil {
		host = "localhost"
	}

	w := hook.gelfLogger

	// remove trailing and leading whitespace
	p := bytes.TrimSpace([]byte(entry.Message))

	// If there are newlines in the message, use the first line
	// for the short message and set the full message to the
	// original input.  If the input has no newlines, stick the
	// whole thing in Short.
	short := p
	full := []byte("")
	if i := bytes.IndexRune(p, '\n'); i > 0 {
		short = p[:i]
		full = p
	}

	level := int32(entry.Level) + 2 // logrus levels are lower than syslog by 2

	// Don't modify entry.Data directly, as the entry will used after this hook was fired
	extra := map[string]interface{}{}
	// Merge extra fields
	for k, v := range hook.Extra {
		k = fmt.Sprintf("_%s", k) // "[...] every field you send and prefix with a _ (underscore) will be treated as an additional field."
		extra[k] = v
	}
	for k, v := range entry.Data {
		k = fmt.Sprintf("_%s", k) // "[...] every field you send and prefix with a _ (underscore) will be treated as an additional field."
		extra[k] = v
	}

	m := gelf.Message{
		Version:  "1.1",
		Host:     host,
		Short:    string(short),
		Full:     string(full),
		TimeUnix: time.Now().UnixNano(),
		Level:    level,
		Facility: hook.Facility,
		File:     entry.file,
		Line:     entry.line,
		Extra:    extra,
	}

	if err := w.WriteMessage(&m); err != nil {
		fmt.Println(err)
	}

}

// Levels returns the available logging levels.
func (hook *GraylogHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
		logrus.WarnLevel,
		logrus.InfoLevel,
		logrus.DebugLevel,
	}
}

// getCaller returns the filename and the line info of a function
// further down in the call stack.  Passing 0 in as callDepth would
// return info on the function calling getCallerIgnoringLog, 1 the
// parent function, and so on.  Any suffixes passed to getCaller are
// path fragments like "/pkg/log/log.go", and functions in the call
// stack from that file are ignored.
func getCaller(callDepth int, suffixesToIgnore ...string) (file string, line int) {
	// bump by 1 to ignore the getCaller (this) stackframe
	callDepth++
outer:
	for {
		var ok bool
		_, file, line, ok = runtime.Caller(callDepth)
		if !ok {
			file = "???"
			line = 0
			break
		}

		for _, s := range suffixesToIgnore {
			if strings.HasSuffix(file, s) {
				callDepth++
				continue outer
			}
		}
		break
	}
	return
}

func getCallerIgnoringLogMulti(callDepth int) (string, int) {
	// the +1 is to ignore this (getCallerIgnoringLogMulti) frame
	return getCaller(callDepth+1, "logrus/hooks.go", "logrus/entry.go", "logrus/logger.go", "logrus/exported.go", "asm_amd64.s")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html"
	"log"
	"os"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
)

// struct to store information about a dish
type dish struct {
	category string
	name     string
}

// Wochentag contains the german names of the weekdays
var Wochentag = [7]string{"Sonntag", "Montag", "Dienstag", "Mittwoch",
	"Donnerstag", "Freitag", "Samstag"}

func main() {
	// Get the date for which we want to fetch the menu.
	// TODO try to handle holidays
	now := time.Now()
	// show mondays menu during the weekend
	if now.Weekday() == time.Saturday {
		now = now.Add(time.Duration(48) * time.Hour)
	} else if now.Weekday() == time.Sunday {
		now = now.Add(time.Duration(24) * time.Hour)
	} else {
		// show next date if it is later than 14:59
		if now.Hour() > 14 {
			// for friday the next date is monday
			if now.Weekday() == time.Friday {
				now = now.Add(time.Duration(72) * time.Hour)
			} else {
				// for all other cases it is simply the next day
				now = now.Add(time.Duration(24) * time.Hour)
			}
		}
	}
	// get string representation of the date
	var date string
	date = now.Format("2006-01-02")

	// TODO different locations
	location := "421"
	// create url
	baseURL := "http://www.studentenwerk-muenchen.de/mensa/speiseplan/"
	menuURL := baseURL + "speiseplan_" + date + "_" + location + "_-de.html"
	// fetch html from Studentenwerk
	doc, err := goquery.NewDocument(menuURL)
	if err != nil {
		log.Fatal(err)
	}
	// extract dishes
	dishes := make([]dish, 0, 6)
	var currentDish dish
	doc.Find(".c-schedule__list-item").Each(func(i int, s *goquery.Selection) {
		// get category (Tagesgericht, Aktionsessen, Beilage, ...)
		currentDish.category = s.Find(".stwm-artname").Text()
		// get dish description
		currentDish.name = s.Find(".js-schedule-dish-description").Text()
		// get rid of the "allergenkennzeichnungspflichtigen" ingredients
		currentDish.name = strings.Split(currentDish.name, "[")[0]
		// we are only interested in main dishes
		if strings.HasPrefix(currentDish.category, "Tagesgericht") ||
			strings.HasPrefix(currentDish.category, "Aktionsessen") ||
			strings.HasPrefix(currentDish.category, "Biogericht") ||
			currentDish.category == "Self-Service" {
			dishes = append(dishes, currentDish)
		}
	})

	outFile, err := os.Create("menu.html")
	if err != nil {
		fmt.Println(err)
	}

	titleString := "Mensa am " + Wochentag[now.Weekday()] + " den " +
		now.Format("02.01.")
	// set title
	outFile.WriteString("
" +
		html.EscapeString(titleString) + "
\n")
	// create box
	outFile.WriteString("
\n")
	// write dishes
	if len(dishes) > 0 {
		for i := range dishes {
			outFile.WriteString("
\n")
			outFile.WriteString("" +
				html.EscapeString(dishes[i].category) + "\n")
			outFile.WriteString("" +
				html.EscapeString(dishes[i].name) + "\n")
			outFile.WriteString("
\n")
		}
	} else {
		// if we failed to fetch any dishes, assume mensa is closed
		outFile.WriteString("Leider geschlossen.")
	}
	// close box
	outFile.WriteString("
\n")
	outFile.Close()
}
<commit_msg>use absolute file path<commit_after>package main

import (
	"fmt"
	"html"
	"log"
	"os"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
)

// struct to store information about a dish
type dish struct {
	category string
	name     string
}

// Wochentag contains the german names of the weekdays
var Wochentag = [7]string{"Sonntag", "Montag", "Dienstag", "Mittwoch",
	"Donnerstag", "Freitag", "Samstag"}

func main() {
	// Get the date for which we want to fetch the menu.
	// TODO try to handle holidays
	now := time.Now()
	// show mondays menu during the weekend
	if now.Weekday() == time.Saturday {
		now = now.Add(time.Duration(48) * time.Hour)
	} else if now.Weekday() == time.Sunday {
		now = now.Add(time.Duration(24) * time.Hour)
	} else {
		// show next date if it is later than 14:59
		if now.Hour() > 14 {
			// for friday the next date is monday
			if now.Weekday() == time.Friday {
				now = now.Add(time.Duration(72) * time.Hour)
			} else {
				// for all other cases it is simply the next day
				now = now.Add(time.Duration(24) * time.Hour)
			}
		}
	}
	// get string representation of the date
	var date string
	date = now.Format("2006-01-02")

	// TODO different locations
	location := "421"
	// create url
	baseURL := "http://www.studentenwerk-muenchen.de/mensa/speiseplan/"
	menuURL := baseURL + "speiseplan_" + date + "_" + location + "_-de.html"
	// fetch html from Studentenwerk
	doc, err := goquery.NewDocument(menuURL)
	if err != nil {
		log.Fatal(err)
	}
	// extract dishes
	dishes := make([]dish, 0, 6)
	var currentDish dish
	doc.Find(".c-schedule__list-item").Each(func(i int, s *goquery.Selection) {
		// get category (Tagesgericht, Aktionsessen, Beilage, ...)
		currentDish.category = s.Find(".stwm-artname").Text()
		// get dish description
		currentDish.name = s.Find(".js-schedule-dish-description").Text()
		// get rid of the "allergenkennzeichnungspflichtigen" ingredients
		currentDish.name = strings.Split(currentDish.name, "[")[0]
		// we are only interested in main dishes
		if strings.HasPrefix(currentDish.category, "Tagesgericht") ||
			strings.HasPrefix(currentDish.category, "Aktionsessen") ||
			strings.HasPrefix(currentDish.category, "Biogericht") ||
			currentDish.category == "Self-Service" {
			dishes = append(dishes, currentDish)
		}
	})

	outFile, err := os.Create("/home/pi/infoscreen/menu.html")
	if err != nil {
		fmt.Println(err)
	}

	titleString := "Mensa am " + Wochentag[now.Weekday()] + " den " +
		now.Format("02.01.")
	// set title
	outFile.WriteString("
" +
		html.EscapeString(titleString) + "
\n")
	// create box
	outFile.WriteString("
\n")
	// write dishes
	if len(dishes) > 0 {
		for i := range dishes {
			outFile.WriteString("
\n")
			outFile.WriteString("" +
				html.EscapeString(dishes[i].category) + "\n")
			outFile.WriteString("" +
				html.EscapeString(dishes[i].name) + "\n")
			outFile.WriteString("
\n")
		}
	} else {
		// if we failed to fetch any dishes, assume mensa is closed
		outFile.WriteString("Leider geschlossen.")
	}
	// close box
	outFile.WriteString("
\n")
	outFile.Close()
}
<|endoftext|>


<commit_before>package providers

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/bitly/oauth2_proxy/cookie"
)

func (p *ProviderData) Redeem(redirectURL, code string) (s *SessionState, err error) {
	if code == "" {
		err = errors.New("missing code")
		return
	}

	params := url.Values{}
	params.Add("redirect_uri", redirectURL)
	params.Add("client_id", p.ClientID)
	params.Add("client_secret", p.ClientSecret)
	params.Add("code", code)
	params.Add("grant_type", "authorization_code")
	if p.ProtectedResource != nil && p.ProtectedResource.String() != "" {
		params.Add("resource", p.ProtectedResource.String())
	}

	var req *http.Request
	req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	var resp *http.Response
	resp, err = http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	var body []byte
	body, err = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return
	}

	if resp.StatusCode != 200 {
		err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemURL.String(), body)
		return
	}

	// blindly try json and x-www-form-urlencoded
	var jsonResponse struct {
		AccessToken string `json:"access_token"`
	}
	err = json.Unmarshal(body, &jsonResponse)
	if err == nil {
		s = &SessionState{
			AccessToken: jsonResponse.AccessToken,
		}
		return
	}

	var v url.Values
	v, err = url.ParseQuery(string(body))
	if err != nil {
		return
	}
	if a := v.Get("access_token"); a != "" {
		s = &SessionState{AccessToken: a}
	} else {
		err = fmt.Errorf("no access token found %s", body)
	}
	return
}

// GetLoginURL with typical oauth parameters
func (p *ProviderData) GetLoginURL(redirectURI, state string) string {
	var a url.URL
	a = *p.LoginURL
	params, _ := url.ParseQuery(a.RawQuery)
	params.Set("redirect_uri", redurectURI)
	//params.Set("approval_prompt", p.ApprovalPrompt)
	//params.Add("scope", "")
	params.Set("client_id", p.ClientID)
	params.Set("response_type", "ID_token")
	params.Add("state", state)
	a.RawQuery = params.Encode()
	return a.String()
}

// CookieForSession serializes a session state for storage in a cookie
func (p *ProviderData) CookieForSession(s *SessionState, c *cookie.Cipher) (string, error) {
	return s.EncodeSessionState(c)
}

// SessionFromCookie deserializes a session from a cookie value
func (p *ProviderData) SessionFromCookie(v string, c *cookie.Cipher) (s *SessionState, err error) {
	return DecodeSessionState(v, c)
}

func (p *ProviderData) GetEmailAddress(s *SessionState) (string, error) {
	return "", errors.New("not implemented")
}

// ValidateGroup validates that the provided email exists in the configured provider
// email group(s).
func (p *ProviderData) ValidateGroup(email string) bool {
	return true
}

func (p *ProviderData) ValidateSessionState(s *SessionState) bool {
	return validateToken(p, s.AccessToken, nil)
}

// RefreshSessionIfNeeded
func (p *ProviderData) RefreshSessionIfNeeded(s *SessionState) (bool, error) {
	return false, nil
}
<commit_msg>fixed typo<commit_after>package providers

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/bitly/oauth2_proxy/cookie"
)

func (p *ProviderData) Redeem(redirectURL, code string) (s *SessionState, err error) {
	if code == "" {
		err = errors.New("missing code")
		return
	}

	params := url.Values{}
	params.Add("redirect_uri", redirectURL)
	params.Add("client_id", p.ClientID)
	params.Add("client_secret", p.ClientSecret)
	params.Add("code", code)
	params.Add("grant_type", "authorization_code")
	if p.ProtectedResource != nil && p.ProtectedResource.String() != "" {
		params.Add("resource", p.ProtectedResource.String())
	}

	var req *http.Request
	req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	var resp *http.Response
	resp, err = http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	var body []byte
	body, err = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return
	}

	if resp.StatusCode != 200 {
		err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemURL.String(), body)
		return
	}

	// blindly try json and x-www-form-urlencoded
	var jsonResponse struct {
		AccessToken string `json:"access_token"`
	}
	err = json.Unmarshal(body, &jsonResponse)
	if err == nil {
		s = &SessionState{
			AccessToken: jsonResponse.AccessToken,
		}
		return
	}

	var v url.Values
	v, err = url.ParseQuery(string(body))
	if err != nil {
		return
	}
	if a := v.Get("access_token"); a != "" {
		s = &SessionState{AccessToken: a}
	} else {
		err = fmt.Errorf("no access token found %s", body)
	}
	return
}

// GetLoginURL with typical oauth parameters
func (p *ProviderData) GetLoginURL(redirectURI, state string) string {
	var a url.URL
	a = *p.LoginURL
	params, _ := url.ParseQuery(a.RawQuery)
	params.Set("redirect_uri", redirectURI)
	//params.Set("approval_prompt", p.ApprovalPrompt)
	//params.Add("scope", "")
	params.Set("client_id", p.ClientID)
	params.Set("response_type", "ID_token")
	params.Add("state", state)
	a.RawQuery = params.Encode()
	return a.String()
}

// CookieForSession serializes a session state for storage in a cookie
func (p *ProviderData) CookieForSession(s *SessionState, c *cookie.Cipher) (string, error) {
	return s.EncodeSessionState(c)
}

// SessionFromCookie deserializes a session from a cookie value
func (p *ProviderData) SessionFromCookie(v string, c *cookie.Cipher) (s *SessionState, err error) {
	return DecodeSessionState(v, c)
}

func (p *ProviderData) GetEmailAddress(s *SessionState) (string, error) {
	return "", errors.New("not implemented")
}

// ValidateGroup validates that the provided email exists in the configured provider
// email group(s).
func (p *ProviderData) ValidateGroup(email string) bool {
	return true
}

func (p *ProviderData) ValidateSessionState(s *SessionState) bool {
	return validateToken(p, s.AccessToken, nil)
}

// RefreshSessionIfNeeded
func (p *ProviderData) RefreshSessionIfNeeded(s *SessionState) (bool, error) {
	return false, nil
}
<|endoftext|>


<commit_before>package bsw

import (
	"crypto/tls"
	"net"
	"time"
)

func TLS(ip string) ([]Result, error) {
	results := make([]Result, 0)
	tconn, err := net.DialTimeout("tcp", ip+":443", 600*time.Millisecond)
	if err != nil {
		return results, err
	}
	config := tls.Config{InsecureSkipVerify: true}
	conn := tls.Client(tconn, &config)
	defer conn.Close()
	if err := conn.Handshake(); err != nil {
		return results, err
	}
	state := conn.ConnectionState()
	cert := state.PeerCertificates[0]
	results = append(results, Result{Source: "TLS Certificate", IP: ip, Hostname: cert.Subject.CommonName})
	for _, name := range cert.DNSNames {
		results = append(results, Result{Source: "TLS Certificate", IP: ip, Hostname: name})
	}
	return results, nil
}
<commit_msg>Removes tls config variable<commit_after>package bsw

import (
	"crypto/tls"
	"net"
	"time"
)

func TLS(ip string) ([]Result, error) {
	results := []Result{}
	tconn, err := net.DialTimeout("tcp", ip+":443", 600*time.Millisecond)
	if err != nil {
		return results, err
	}
	conn := tls.Client(tconn, &tls.Config{InsecureSkipVerify: true})
	defer conn.Close()
	if err := conn.Handshake(); err != nil {
		return results, err
	}
	state := conn.ConnectionState()
	cert := state.PeerCertificates[0]
	results = append(results, Result{Source: "TLS Certificate", IP: ip, Hostname: cert.Subject.CommonName})
	for _, name := range cert.DNSNames {
		results = append(results, Result{Source: "TLS Certificate", IP: ip, Hostname: name})
	}
	return results, nil
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time_test

import (
	"errors"
	"fmt"
	"runtime"
	"sort"
	"sync/atomic"
	"testing"
	. "time"
)

func TestSleep(t *testing.T) {
	const delay = 100 * Millisecond
	go func() {
		Sleep(delay / 2)
		Interrupt()
	}()
	start := Now()
	Sleep(delay)
	duration := Now().Sub(start)
	if duration < delay {
		t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
	}
}

// Test the basic function calling behavior. Correct queueing
// behavior is tested elsewhere, since After and AfterFunc share
// the same code.
func TestAfterFunc(t *testing.T) {
	i := 10
	c := make(chan bool)
	var f func()
	f = func() {
		i--
		if i >= 0 {
			AfterFunc(0, f)
			Sleep(1 * Second)
		} else {
			c <- true
		}
	}

	AfterFunc(0, f)
	<-c
}

func TestAfterStress(t *testing.T) {
	stop := uint32(0)
	go func() {
		for atomic.LoadUint32(&stop) == 0 {
			runtime.GC()
			// Need to yield, because otherwise
			// the main goroutine will never set the stop flag.
			runtime.Gosched()
		}
	}()
	c := Tick(1)
	for i := 0; i < 100; i++ {
		<-c
	}
	atomic.StoreUint32(&stop, 1)
}

func BenchmarkAfterFunc(b *testing.B) {
	i := b.N
	c := make(chan bool)
	var f func()
	f = func() {
		i--
		if i >= 0 {
			AfterFunc(0, f)
		} else {
			c <- true
		}
	}

	AfterFunc(0, f)
	<-c
}

func BenchmarkAfter(b *testing.B) {
	for i := 0; i < b.N; i++ {
		<-After(1)
	}
}

func BenchmarkStop(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewTimer(1 * Second).Stop()
	}
}

func TestAfter(t *testing.T) {
	const delay = 100 * Millisecond
	start := Now()
	end := <-After(delay)
	if duration := Now().Sub(start); duration < delay {
		t.Fatalf("After(%s) slept for only %d ns", delay, duration)
	}
	if min := start.Add(delay); end.Before(min) {
		t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
	}
}

func TestAfterTick(t *testing.T) {
	const Count = 10
	Delta := 100 * Millisecond
	if testing.Short() {
		Delta = 10 * Millisecond
	}
	t0 := Now()
	for i := 0; i < Count; i++ {
		<-After(Delta)
	}
	t1 := Now()
	d := t1.Sub(t0)
	target := Delta * Count
	if d < target*9/10 {
		t.Fatalf("%d ticks of %s too fast: took %s, expected %s", Count, Delta, d, target)
	}
	if !testing.Short() && d > target*30/10 {
		t.Fatalf("%d ticks of %s too slow: took %s, expected %s", Count, Delta, d, target)
	}
}

func TestAfterStop(t *testing.T) {
	AfterFunc(100*Millisecond, func() {})
	t0 := NewTimer(50 * Millisecond)
	c1 := make(chan bool, 1)
	t1 := AfterFunc(150*Millisecond, func() { c1 <- true })
	c2 := After(200 * Millisecond)
	if !t0.Stop() {
		t.Fatalf("failed to stop event 0")
	}
	if !t1.Stop() {
		t.Fatalf("failed to stop event 1")
	}
	<-c2
	select {
	case <-t0.C:
		t.Fatalf("event 0 was not stopped")
	case <-c1:
		t.Fatalf("event 1 was not stopped")
	default:
	}
	if t1.Stop() {
		t.Fatalf("Stop returned true twice")
	}
}

func TestAfterQueuing(t *testing.T) {
	// This test flakes out on some systems,
	// so we'll try it a few times before declaring it a failure.
	const attempts = 3
	err := errors.New("!=nil")
	for i := 0; i < attempts && err != nil; i++ {
		if err = testAfterQueuing(t); err != nil {
			t.Logf("attempt %v failed: %v", i, err)
		}
	}
	if err != nil {
		t.Fatal(err)
	}
}

var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0}

type afterResult struct {
	slot int
	t    Time
}

func await(slot int, result chan<- afterResult, ac <-chan Time) {
	result <- afterResult{slot, <-ac}
}

func testAfterQueuing(t *testing.T) error {
	Delta := 100 * Millisecond
	if testing.Short() {
		Delta = 20 * Millisecond
	}
	// make the result channel buffered because we don't want
	// to depend on channel queueing semantics that might
	// possibly change in the future.
	result := make(chan afterResult, len(slots))

	t0 := Now()
	for _, slot := range slots {
		go await(slot, result, After(Duration(slot)*Delta))
	}
	sort.Ints(slots)
	for _, slot := range slots {
		r := <-result
		if r.slot != slot {
			return fmt.Errorf("after slot %d, expected %d", r.slot, slot)
		}
		dt := r.t.Sub(t0)
		target := Duration(slot) * Delta
		if dt < target-Delta/2 || dt > target+Delta*10 {
			return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-Delta/2, target+Delta*10)
		}
	}
	return nil
}

func TestTimerStopStress(t *testing.T) {
	if testing.Short() {
		return
	}
	for i := 0; i < 100; i++ {
		go func(i int) {
			timer := AfterFunc(2*Second, func() {
				t.Fatalf("timer %d was not stopped", i)
			})
			Sleep(1 * Second)
			timer.Stop()
		}(i)
	}
	Sleep(3 * Second)
}

func TestSleepZeroDeadlock(t *testing.T) {
	// Sleep(0) used to hang, the sequence of events was as follows.
	// Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
	// Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
	// After the GC nobody wakes up the goroutine from Gwaiting status.
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
	c := make(chan bool)
	go func() {
		for i := 0; i < 100; i++ {
			runtime.GC()
		}
		c <- true
	}()
	for i := 0; i < 100; i++ {
		Sleep(0)
		tmp := make(chan bool, 1)
		tmp <- true
		<-tmp
	}
	<-c
}

func TestReset(t *testing.T) {
	t0 := NewTimer(100 * Millisecond)
	Sleep(50 * Millisecond)
	if t0.Reset(150*Millisecond) != true {
		t.Fatalf("resetting unfired timer returned false")
	}
	Sleep(100 * Millisecond)
	select {
	case <-t0.C:
		t.Fatalf("timer fired early")
	default:
	}
	Sleep(100 * Millisecond)
	select {
	case <-t0.C:
	default:
		t.Fatalf("reset timer did not fire")
	}

	if t0.Reset(50*Millisecond) != false {
		t.Fatalf("resetting expired timer returned true")
	}
}
<commit_msg>time: Sleep does better job then runtime.Gosched in TestAfterStress<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time_test

import (
	"errors"
	"fmt"
	"runtime"
	"sort"
	"sync/atomic"
	"testing"
	. "time"
)

func TestSleep(t *testing.T) {
	const delay = 100 * Millisecond
	go func() {
		Sleep(delay / 2)
		Interrupt()
	}()
	start := Now()
	Sleep(delay)
	duration := Now().Sub(start)
	if duration < delay {
		t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
	}
}

// Test the basic function calling behavior. Correct queueing
// behavior is tested elsewhere, since After and AfterFunc share
// the same code.
func TestAfterFunc(t *testing.T) {
	i := 10
	c := make(chan bool)
	var f func()
	f = func() {
		i--
		if i >= 0 {
			AfterFunc(0, f)
			Sleep(1 * Second)
		} else {
			c <- true
		}
	}

	AfterFunc(0, f)
	<-c
}

func TestAfterStress(t *testing.T) {
	stop := uint32(0)
	go func() {
		for atomic.LoadUint32(&stop) == 0 {
			runtime.GC()
			// Yield so that the OS can wake up the timer thread,
			// so that it can generate channel sends for the main goroutine,
			// which will eventually set stop = 1 for us.
			Sleep(Nanosecond)
		}
	}()
	c := Tick(1)
	for i := 0; i < 100; i++ {
		<-c
	}
	atomic.StoreUint32(&stop, 1)
}

func BenchmarkAfterFunc(b *testing.B) {
	i := b.N
	c := make(chan bool)
	var f func()
	f = func() {
		i--
		if i >= 0 {
			AfterFunc(0, f)
		} else {
			c <- true
		}
	}

	AfterFunc(0, f)
	<-c
}

func BenchmarkAfter(b *testing.B) {
	for i := 0; i < b.N; i++ {
		<-After(1)
	}
}

func BenchmarkStop(b *testing.B) {
	for i := 0; i < b.N; i++ {
		NewTimer(1 * Second).Stop()
	}
}

func TestAfter(t *testing.T) {
	const delay = 100 * Millisecond
	start := Now()
	end := <-After(delay)
	if duration := Now().Sub(start); duration < delay {
		t.Fatalf("After(%s) slept for only %d ns", delay, duration)
	}
	if min := start.Add(delay); end.Before(min) {
		t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
	}
}

func TestAfterTick(t *testing.T) {
	const Count = 10
	Delta := 100 * Millisecond
	if testing.Short() {
		Delta = 10 * Millisecond
	}
	t0 := Now()
	for i := 0; i < Count; i++ {
		<-After(Delta)
	}
	t1 := Now()
	d := t1.Sub(t0)
	target := Delta * Count
	if d < target*9/10 {
		t.Fatalf("%d ticks of %s too fast: took %s, expected %s", Count, Delta, d, target)
	}
	if !testing.Short() && d > target*30/10 {
		t.Fatalf("%d ticks of %s too slow: took %s, expected %s", Count, Delta, d, target)
	}
}

func TestAfterStop(t *testing.T) {
	AfterFunc(100*Millisecond, func() {})
	t0 := NewTimer(50 * Millisecond)
	c1 := make(chan bool, 1)
	t1 := AfterFunc(150*Millisecond, func() { c1 <- true })
	c2 := After(200 * Millisecond)
	if !t0.Stop() {
		t.Fatalf("failed to stop event 0")
	}
	if !t1.Stop() {
		t.Fatalf("failed to stop event 1")
	}
	<-c2
	select {
	case <-t0.C:
		t.Fatalf("event 0 was not stopped")
	case <-c1:
		t.Fatalf("event 1 was not stopped")
	default:
	}
	if t1.Stop() {
		t.Fatalf("Stop returned true twice")
	}
}

func TestAfterQueuing(t *testing.T) {
	// This test flakes out on some systems,
	// so we'll try it a few times before declaring it a failure.
	const attempts = 3
	err := errors.New("!=nil")
	for i := 0; i < attempts && err != nil; i++ {
		if err = testAfterQueuing(t); err != nil {
			t.Logf("attempt %v failed: %v", i, err)
		}
	}
	if err != nil {
		t.Fatal(err)
	}
}

var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0}

type afterResult struct {
	slot int
	t    Time
}

func await(slot int, result chan<- afterResult, ac <-chan Time) {
	result <- afterResult{slot, <-ac}
}

func testAfterQueuing(t *testing.T) error {
	Delta := 100 * Millisecond
	if testing.Short() {
		Delta = 20 * Millisecond
	}
	// make the result channel buffered because we don't want
	// to depend on channel queueing semantics that might
	// possibly change in the future.
	result := make(chan afterResult, len(slots))

	t0 := Now()
	for _, slot := range slots {
		go await(slot, result, After(Duration(slot)*Delta))
	}
	sort.Ints(slots)
	for _, slot := range slots {
		r := <-result
		if r.slot != slot {
			return fmt.Errorf("after slot %d, expected %d", r.slot, slot)
		}
		dt := r.t.Sub(t0)
		target := Duration(slot) * Delta
		if dt < target-Delta/2 || dt > target+Delta*10 {
			return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-Delta/2, target+Delta*10)
		}
	}
	return nil
}

func TestTimerStopStress(t *testing.T) {
	if testing.Short() {
		return
	}
	for i := 0; i < 100; i++ {
		go func(i int) {
			timer := AfterFunc(2*Second, func() {
				t.Fatalf("timer %d was not stopped", i)
			})
			Sleep(1 * Second)
			timer.Stop()
		}(i)
	}
	Sleep(3 * Second)
}

func TestSleepZeroDeadlock(t *testing.T) {
	// Sleep(0) used to hang, the sequence of events was as follows.
	// Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
	// Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
	// After the GC nobody wakes up the goroutine from Gwaiting status.
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
	c := make(chan bool)
	go func() {
		for i := 0; i < 100; i++ {
			runtime.GC()
		}
		c <- true
	}()
	for i := 0; i < 100; i++ {
		Sleep(0)
		tmp := make(chan bool, 1)
		tmp <- true
		<-tmp
	}
	<-c
}

func TestReset(t *testing.T) {
	t0 := NewTimer(100 * Millisecond)
	Sleep(50 * Millisecond)
	if t0.Reset(150*Millisecond) != true {
		t.Fatalf("resetting unfired timer returned false")
	}
	Sleep(100 * Millisecond)
	select {
	case <-t0.C:
		t.Fatalf("timer fired early")
	default:
	}
	Sleep(100 * Millisecond)
	select {
	case <-t0.C:
	default:
		t.Fatalf("reset timer did not fire")
	}

	if t0.Reset(50*Millisecond) != false {
		t.Fatalf("resetting expired timer returned true")
	}
}
<|endoftext|>


<commit_before>//
// Copyright 2015 The CoreOS 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime/debug"

	"github.com/coreos/coreos-cloudinit/config/validate"
	"github.com/crawford/nap"
	"github.com/gorilla/mux"
)

var (
	flags = struct {
		port    int
		address string
	}{}
)

type payloadWrapper struct{}

func (w payloadWrapper) Wrap(payload interface{}, status nap.Status) (interface{}, int) {
	return map[string]interface{}{
		"result": payload,
	}, status.Code()
}

type panicHandler struct{}

func (h panicHandler) Handle(e interface{}) {
	log.Printf("PANIC: %#v\n", e)
	debug.PrintStack()
}

func init() {
	flag.StringVar(&flags.address, "address", "0.0.0.0", "address to listen on")
	flag.IntVar(&flags.port, "port", 80, "port to bind on")

	nap.PayloadWrapper = payloadWrapper{}
	nap.PanicHandler = panicHandler{}
	nap.ResponseHeaders = []nap.Header{
		nap.Header{"Access-Control-Allow-Origin", []string{"*"}},
		nap.Header{"Access-Control-Allow-Methods", []string{"OPTIONS, PUT"}},
		nap.Header{"Content-Type", []string{"application/json"}},
		nap.Header{"Cache-Control", []string{"no-cache,must-revalidate"}},
	}
}

func main() {
	flag.Parse()

	router := mux.NewRouter()
	server := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", flags.address, flags.port),
		Handler: router,
	}

	router.Handle("/validate", nap.HandlerFunc(optionsValidate)).Methods("OPTIONS")
	router.Handle("/validate", nap.HandlerFunc(putValidate)).Methods("PUT")
	router.Handle("/health", nap.HandlerFunc(getHealth)).Methods("GET")

	log.Fatalln(server.ListenAndServe())
}

func optionsValidate(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}

func putValidate(r *http.Request) (interface{}, nap.Status) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, nap.InternalError{err.Error()}
	}

	config := bytes.Replace(body, []byte("\r"), []byte{}, -1)
	if report, err := validate.Validate(config); err == nil {
		return report.Entries(), nap.OK{}
	} else {
		return nil, nap.InternalError{err.Error()}
	}
}

func getHealth(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}
<commit_msg>validate: add ignition validation<commit_after>//
// Copyright 2015 The CoreOS 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime/debug"

	"github.com/coreos/coreos-cloudinit/config/validate"
	ignConfig "github.com/coreos/ignition/config"
	"github.com/crawford/nap"
	"github.com/gorilla/mux"
)

var (
	flags = struct {
		port    int
		address string
	}{}
)

type payloadWrapper struct{}

func (w payloadWrapper) Wrap(payload interface{}, status nap.Status) (interface{}, int) {
	return map[string]interface{}{
		"result": payload,
	}, status.Code()
}

type panicHandler struct{}

func (h panicHandler) Handle(e interface{}) {
	log.Printf("PANIC: %#v\n", e)
	debug.PrintStack()
}

func init() {
	flag.StringVar(&flags.address, "address", "0.0.0.0", "address to listen on")
	flag.IntVar(&flags.port, "port", 80, "port to bind on")

	nap.PayloadWrapper = payloadWrapper{}
	nap.PanicHandler = panicHandler{}
	nap.ResponseHeaders = []nap.Header{
		nap.Header{"Access-Control-Allow-Origin", []string{"*"}},
		nap.Header{"Access-Control-Allow-Methods", []string{"OPTIONS, PUT"}},
		nap.Header{"Content-Type", []string{"application/json"}},
		nap.Header{"Cache-Control", []string{"no-cache,must-revalidate"}},
	}
}

func main() {
	flag.Parse()

	router := mux.NewRouter()
	server := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", flags.address, flags.port),
		Handler: router,
	}

	router.Handle("/validate", nap.HandlerFunc(optionsValidate)).Methods("OPTIONS")
	router.Handle("/validate", nap.HandlerFunc(putValidate)).Methods("PUT")
	router.Handle("/health", nap.HandlerFunc(getHealth)).Methods("GET")

	log.Fatalln(server.ListenAndServe())
}

func optionsValidate(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}

func putValidate(r *http.Request) (interface{}, nap.Status) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, nap.InternalError{err.Error()}
	}

	config := bytes.Replace(body, []byte("\r"), []byte{}, -1)

	_, report, err := ignConfig.Parse(config)
	switch err {
	case ignConfig.ErrCloudConfig, ignConfig.ErrEmpty, ignConfig.ErrScript:
		report, err := validate.Validate(config)
		if err != nil {
			return nil, nap.InternalError{err.Error()}
		}
		return report.Entries(), nap.OK{}
	default:
		report.Sort()
		return report.Entries, nap.OK{}
	}
}

func getHealth(r *http.Request) (interface{}, nap.Status) {
	return nil, nap.OK{}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
)

var (
	startsWithSpaces   = regexp.MustCompile(`^\s+`)
	fourSpacesIndented = regexp.MustCompile(`^([ ]{4}).[^\s]+`)
	endsWithSpaces     = regexp.MustCompile(`\s+$`)
)

func validateFile(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	scanner := bufio.NewScanner(reader)

	num := 0
	for scanner.Scan() {
		line := scanner.Text()
		num++

		err := validateLine(line)

		if err != nil {
			fmt.Printf("%s:%d\t%s\n", filename, num, err.Error())
		}
	}
}

func validateLine(line string) error {
	if startsWithSpaces.MatchString(line) && !fourSpacesIndented.MatchString(line) {
		return errors.New("Not indented using 4 spaces")
	}

	if endsWithSpaces.MatchString(line) {
		return errors.New("Trailing whitespaces at the end of the line")
	}

	if strings.Trim(line, " ") == "{" {
		return errors.New("First parenthese isn't on the end of the first line")
	}

	return nil
}
<commit_msg>Improvements on the validation code<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
)

var (
	startsWithSpaces   = regexp.MustCompile(`^\s+`)
	fourSpacesIndented = regexp.MustCompile(`^([ ]{4}).[^\s]+`)
	endsWithSpaces     = regexp.MustCompile(`\s+$`)
)

func validateFile(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	scanner := bufio.NewScanner(reader)

	num := 0
	for scanner.Scan() {
		line := scanner.Text()
		num++

		warnings := validateLine(line)

		if len(warnings) > 0 {
			for _, warning := range warnings {
				fmt.Printf("%s:%d\t%s\n", filename, num, warning)
			}
		}
	}
}

func validateLine(line string) []string {
	var warnings []string
	if strings.Trim(line, " ") == "{" {
		warnings = append(warnings, "First parenthese isn't on the end of the first line")
	}

	if endsWithSpaces.MatchString(line) {
		warnings = append(warnings, "Trailing whitespaces at the end of the line")
	}

	if startsWithSpaces.MatchString(line) && !fourSpacesIndented.MatchString(line) {
		warnings = append(warnings, "Not indented using 4 spaces")
	}

	return warnings
}
<|endoftext|>


<commit_before>package svnwatch

import (
	"encoding/xml"
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/jackwilsdon/svnwatch/svn"
	shellwords "github.com/mattn/go-shellwords"
	"github.com/pkg/errors"
)

type Command struct {
	XMLName      xml.Name `xml:"command"`
	ArgumentType string   `xml:"argument-type,attr,omitempty"`
	Command      string   `xml:",chardata"`
}

func (c Command) Execute(repo Repository, revision svn.Revision) error {
	pieces, err := shellwords.Parse(c.Command)

	if err != nil {
		return errors.Wrapf(err, "failed to parse \"%s\"", c.Command)
	}

	if len(pieces) == 0 {
		return fmt.Errorf("failed to parse \"%s\"", c.Command)
	}

	cmd := exec.Command(pieces[0], pieces[1:]...)
	cmd.Env = os.Environ()

	if c.ArgumentType == "normal" {
		cmd.Args = append(cmd.Args, repo.URL, strconv.Itoa(repo.Revision))
	} else if c.ArgumentType == "env" {
		cmd.Env = append(
			os.Environ(),
			fmt.Sprintf("SVN_URL=%s", repo.URL),
			fmt.Sprintf("SVN_REVISION=%d", repo.Revision),
		)
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", c.ArgumentType, c.Command)
	}

	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to execute \"%s\"", c.Command)
	}

	return nil
}

func (c *Command) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	cmd := struct {
		ArgumentType *string `xml:"argument-type,attr,omitempty"`
		Command      string  `xml:",chardata"`
	}{}

	if err := d.DecodeElement(&cmd, &start); err != nil {
		return err
	}

	if cmd.ArgumentType == nil {
		c.ArgumentType = "normal"
	} else if *cmd.ArgumentType == "normal" || *cmd.ArgumentType == "env" {
		c.ArgumentType = *cmd.ArgumentType
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", *cmd.ArgumentType, cmd.Command)
	}

	c.Command = cmd.Command

	return nil
}
<commit_msg>Use passed revision for commands<commit_after>package svnwatch

import (
	"encoding/xml"
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/jackwilsdon/svnwatch/svn"
	shellwords "github.com/mattn/go-shellwords"
	"github.com/pkg/errors"
)

type Command struct {
	XMLName      xml.Name `xml:"command"`
	ArgumentType string   `xml:"argument-type,attr,omitempty"`
	Command      string   `xml:",chardata"`
}

func (c Command) Execute(repo Repository, revision svn.Revision) error {
	pieces, err := shellwords.Parse(c.Command)

	if err != nil {
		return errors.Wrapf(err, "failed to parse \"%s\"", c.Command)
	}

	if len(pieces) == 0 {
		return fmt.Errorf("failed to parse \"%s\"", c.Command)
	}

	cmd := exec.Command(pieces[0], pieces[1:]...)
	cmd.Env = os.Environ()

	if c.ArgumentType == "normal" {
		cmd.Args = append(cmd.Args, repo.URL, strconv.Itoa(revision.Revision))
	} else if c.ArgumentType == "env" {
		cmd.Env = append(
			os.Environ(),
			fmt.Sprintf("SVN_URL=%s", repo.URL),
			fmt.Sprintf("SVN_REVISION=%d", revision.Revision),
		)
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", c.ArgumentType, c.Command)
	}

	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to execute \"%s\"", c.Command)
	}

	return nil
}

func (c *Command) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	cmd := struct {
		ArgumentType *string `xml:"argument-type,attr,omitempty"`
		Command      string  `xml:",chardata"`
	}{}

	if err := d.DecodeElement(&cmd, &start); err != nil {
		return err
	}

	if cmd.ArgumentType == nil {
		c.ArgumentType = "normal"
	} else if *cmd.ArgumentType == "normal" || *cmd.ArgumentType == "env" {
		c.ArgumentType = *cmd.ArgumentType
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", *cmd.ArgumentType, cmd.Command)
	}

	c.Command = cmd.Command

	return nil
}
<|endoftext|>


<commit_before>package handler

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"

	"code.google.com/p/go.crypto/bcrypt"
	"github.com/gorilla/mux"
	"github.com/hackedu/backend/database"
	"github.com/hackedu/backend/model"
)

// Authenticate checks the provided user information against the information
// in the database. If it all checks out, then a JWT is generated and
// returned.
func Authenticate(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	defer r.Body.Close()

	var requestUser model.RequestUser
	err := json.NewDecoder(r.Body).Decode(&requestUser)
	if err != nil {
		return ErrUnmarshalling(err)
	}

	userFromDB, err := database.GetUserByEmail(requestUser.Email)
	if err != nil {
		if err == sql.ErrNoRows {
			return ErrNotFound(err)
		}
		return ErrDatabase(err)
	}

	err = userFromDB.ComparePassword(requestUser.Password)
	if err == bcrypt.ErrMismatchedHashAndPassword {
		return &AppError{err, "invalid password", http.StatusBadRequest}
	} else if err != nil {
		return &AppError{err, "error checking password",
			http.StatusInternalServerError}
	}

	token, err := model.NewToken(userFromDB)
	if err != nil {
		return &AppError{err, "problem creating jwt token",
			http.StatusInternalServerError}
	}

	return renderJSON(w, token, http.StatusOK)
}

// CreateUser creates a new user from JSON in the request body.
func CreateUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	if u == nil || u.Type != model.UserAdmin {
		return ErrNotAuthorized()
	}

	defer r.Body.Close()
	user, err := model.NewUser(r.Body)
	if err != nil {
		return ErrCreatingModel(err)
	}

	err = database.SaveUser(user)
	if err != nil {
		if err == model.ErrInvalidUserEmail {
			return ErrCreatingModel(err)
		}
		return ErrDatabase(err)
	}

	return renderJSON(w, user, http.StatusOK)
}

// GetUser gets the user specified by ID in the url.
func GetUser(w http.ResponseWriter, r *http.Request, u *model.User) *AppError {
	vars := mux.Vars(r)
	stringID := vars["id"]

	var id int64
	if stringID == "me" {
		if u == nil {
			return ErrNotAuthorized()
		}

		id = u.ID
	} else {
		var err error
		id, err = strconv.ParseInt(vars["id"], 10, 64)
		if err != nil {
			return ErrInvalidID(err)
		}
	}

	if id == u.ID {
		return renderJSON(w, u, http.StatusOK)
	}

	return ErrNotAuthorized()
}

// GetCurrentUser gets the current authenticated user.
func GetCurrentUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	if u == nil {
		return ErrNotAuthorized()
	}

	return renderJSON(w, u, http.StatusOK)
}
<commit_msg>Update handler.GetUser to handle the different User types.<commit_after>package handler

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"

	"code.google.com/p/go.crypto/bcrypt"
	"github.com/gorilla/mux"
	"github.com/hackedu/backend/database"
	"github.com/hackedu/backend/model"
)

// Authenticate checks the provided user information against the information
// in the database. If it all checks out, then a JWT is generated and
// returned.
func Authenticate(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	defer r.Body.Close()

	var requestUser model.RequestUser
	err := json.NewDecoder(r.Body).Decode(&requestUser)
	if err != nil {
		return ErrUnmarshalling(err)
	}

	userFromDB, err := database.GetUserByEmail(requestUser.Email)
	if err != nil {
		if err == sql.ErrNoRows {
			return ErrNotFound(err)
		}
		return ErrDatabase(err)
	}

	err = userFromDB.ComparePassword(requestUser.Password)
	if err == bcrypt.ErrMismatchedHashAndPassword {
		return &AppError{err, "invalid password", http.StatusBadRequest}
	} else if err != nil {
		return &AppError{err, "error checking password",
			http.StatusInternalServerError}
	}

	token, err := model.NewToken(userFromDB)
	if err != nil {
		return &AppError{err, "problem creating jwt token",
			http.StatusInternalServerError}
	}

	return renderJSON(w, token, http.StatusOK)
}

// CreateUser creates a new user from JSON in the request body.
func CreateUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	if u == nil || u.Type != model.UserAdmin {
		return ErrNotAuthorized()
	}

	defer r.Body.Close()
	user, err := model.NewUser(r.Body)
	if err != nil {
		return ErrCreatingModel(err)
	}

	err = database.SaveUser(user)
	if err != nil {
		if err == model.ErrInvalidUserEmail {
			return ErrCreatingModel(err)
		}
		return ErrDatabase(err)
	}

	return renderJSON(w, user, http.StatusOK)
}

// GetUser gets the user specified by ID in the url. If the user is an admin,
// they can see any profile. If the user is an organizer or a member, they can
// only view their own profile. If they are not authorized, they cannot see
// any profiles.
func GetUser(w http.ResponseWriter, r *http.Request, u *model.User) *AppError {
	if u == nil {
		return ErrNotAuthorized()
	}

	vars := mux.Vars(r)
	stringID := vars["id"]

	var id int64
	if stringID == "me" {
		id = u.ID
	} else {
		var err error
		id, err = strconv.ParseInt(vars["id"], 10, 64)
		if err != nil {
			return ErrInvalidID(err)
		}
	}

	if u.Type == model.UserAdmin {
		return renderJSON(w, u, http.StatusOK)
	} else {
		if id == u.ID {
			return renderJSON(w, u, http.StatusOK)
		}

		return ErrNotAuthorized()
	}
}

// GetCurrentUser gets the current authenticated user.
func GetCurrentUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	if u == nil {
		return ErrNotAuthorized()
	}

	return renderJSON(w, u, http.StatusOK)
}
<|endoftext|>


<commit_before>package hashids

import (
	"math"
	"reflect"
	"testing"
)

func TestEncodeDecode(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 30
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.Decode(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestEncodeDecodeInt64(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 30
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int64{45, 434, 1313, 99, math.MaxInt64}
	hash, err := hid.EncodeInt64(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.DecodeInt64(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestEncodeWithKnownHash(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 0
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}

	t.Logf("%v -> %v", numbers, hash)

	if hash != "7nnhzEsDkiYa" {
		t.Error("hash does not match expected one")
	}
}

func TestDecodeWithKnownHash(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 0
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	hash := "7nnhzEsDkiYa"
	numbers := hid.Decode(hash)

	t.Logf("%v -> %v", hash, numbers)

	expected := []int{45, 434, 1313, 99}
	if !reflect.DeepEqual(numbers, expected) {
		t.Errorf("Decoded numbers `%v` did not match with expected `%v`", numbers, expected)
	}
}

func TestDefaultLength(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.Decode(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestMinLength(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "salt1"
	hdata.MinLength = 10
	hid, _ := NewWithData(hdata)
	hid.Encode([]int{0})
}

func TestCustomAlphabet(t *testing.T) {
	hdata := NewData()
	hdata.Alphabet = "PleasAkMEFoThStx"
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.Decode(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestDecodeWithError(t *testing.T) {
	hdata := NewData()
	hdata.Alphabet = "PleasAkMEFoThStx"
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)
	// hash now contains a letter not in the alphabet
	dec, err := hid.DecodeWithError("MAkhkloFAxAoskaZ")

	if dec != nil {
		t.Error("Expected `nil` but got `%v`", dec)
	}
	expected := "alphabet used for hash was different"
	if err == nil || err.Error() != expected {
		t.Errorf("Expected error `%s` but got `%s`", expected, err)
	}
}

// tests issue #28
func TestDecodeWithWrongSalt(t *testing.T) {
	hdata := NewData()
	hdata.Alphabet = "PleasAkMEFoThStx"
	hdata.Salt = "temp"

	hidEncode, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, _ := hidEncode.Encode(numbers)

	hdata.Salt = "test"
	hidDecode, _ := NewWithData(hdata)
	dec, err := hidDecode.DecodeWithError(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	expected := "mismatch between encode and decode: ePaTMalsPMPlhxMl start MEhloASEPosaE re-encoded. result: [7 199 245 19]"
	if err == nil || err.Error() != expected {
		t.Errorf("Expected error `%s` but got `%s`", expected, err)
	}
}

func checkAllocations(t *testing.T, hid *HashID, values []int64, expectedAllocations float64) {
	allocsPerRun := testing.AllocsPerRun(5, func() {
		_, err := hid.EncodeInt64(values)
		if err != nil {
			t.Errorf("Unexpected error encoding test data: %s, %v", err, values)
		}
	})
	if allocsPerRun != expectedAllocations {
		t.Errorf("Expected %v allocations, got %v ", expectedAllocations, allocsPerRun)
	}
}

func TestAllocationsPerEncodeTypical(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "temp"
	hdata.MinLength = 0
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 5)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 5)
	checkAllocations(t, hid, minNumbers, 5)
	checkAllocations(t, hid, mixNubers, 5)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 5)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 5)
}

func TestAllocationsPerEncodeNoSalt(t *testing.T) {
	hdata := NewData()
	hdata.Salt = ""
	hdata.MinLength = 0
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 5)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 5)
	checkAllocations(t, hid, minNumbers, 5)
	checkAllocations(t, hid, mixNubers, 5)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 5)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 5)
}

func TestAllocationsPerEncodeMinLength(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "temp"
	hdata.MinLength = 10
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 9)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 5)
	checkAllocations(t, hid, minNumbers, 6)
	checkAllocations(t, hid, mixNubers, 5)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 5)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 5)
}

func TestAllocationsPerEncodeMinLengthHigh(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "temp"
	hdata.MinLength = 100
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 15)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 12)
	checkAllocations(t, hid, minNumbers, 15)
	checkAllocations(t, hid, mixNubers, 12)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 12)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 9)
}
<commit_msg>Test for allocations during decode<commit_after>package hashids

import (
	"math"
	"reflect"
	"testing"
)

func TestEncodeDecode(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 30
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.Decode(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestEncodeDecodeInt64(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 30
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int64{45, 434, 1313, 99, math.MaxInt64}
	hash, err := hid.EncodeInt64(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.DecodeInt64(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestEncodeWithKnownHash(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 0
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}

	t.Logf("%v -> %v", numbers, hash)

	if hash != "7nnhzEsDkiYa" {
		t.Error("hash does not match expected one")
	}
}

func TestDecodeWithKnownHash(t *testing.T) {
	hdata := NewData()
	hdata.MinLength = 0
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	hash := "7nnhzEsDkiYa"
	numbers := hid.Decode(hash)

	t.Logf("%v -> %v", hash, numbers)

	expected := []int{45, 434, 1313, 99}
	if !reflect.DeepEqual(numbers, expected) {
		t.Errorf("Decoded numbers `%v` did not match with expected `%v`", numbers, expected)
	}
}

func TestDefaultLength(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.Decode(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestMinLength(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "salt1"
	hdata.MinLength = 10
	hid, _ := NewWithData(hdata)
	hid.Encode([]int{0})
}

func TestCustomAlphabet(t *testing.T) {
	hdata := NewData()
	hdata.Alphabet = "PleasAkMEFoThStx"
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, err := hid.Encode(numbers)
	if err != nil {
		t.Fatal(err)
	}
	dec := hid.Decode(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	if !reflect.DeepEqual(dec, numbers) {
		t.Errorf("Decoded numbers `%v` did not match with original `%v`", dec, numbers)
	}
}

func TestDecodeWithError(t *testing.T) {
	hdata := NewData()
	hdata.Alphabet = "PleasAkMEFoThStx"
	hdata.Salt = "this is my salt"

	hid, _ := NewWithData(hdata)
	// hash now contains a letter not in the alphabet
	dec, err := hid.DecodeWithError("MAkhkloFAxAoskaZ")

	if dec != nil {
		t.Error("Expected `nil` but got `%v`", dec)
	}
	expected := "alphabet used for hash was different"
	if err == nil || err.Error() != expected {
		t.Errorf("Expected error `%s` but got `%s`", expected, err)
	}
}

// tests issue #28
func TestDecodeWithWrongSalt(t *testing.T) {
	hdata := NewData()
	hdata.Alphabet = "PleasAkMEFoThStx"
	hdata.Salt = "temp"

	hidEncode, _ := NewWithData(hdata)

	numbers := []int{45, 434, 1313, 99}
	hash, _ := hidEncode.Encode(numbers)

	hdata.Salt = "test"
	hidDecode, _ := NewWithData(hdata)
	dec, err := hidDecode.DecodeWithError(hash)

	t.Logf("%v -> %v -> %v", numbers, hash, dec)

	expected := "mismatch between encode and decode: ePaTMalsPMPlhxMl start MEhloASEPosaE re-encoded. result: [7 199 245 19]"
	if err == nil || err.Error() != expected {
		t.Errorf("Expected error `%s` but got `%s`", expected, err)
	}
}

func checkAllocations(t *testing.T, hid *HashID, values []int64, expectedAllocations float64) {
	allocsPerRun := testing.AllocsPerRun(5, func() {
		_, err := hid.EncodeInt64(values)
		if err != nil {
			t.Errorf("Unexpected error encoding test data: %s, %v", err, values)
		}
	})
	if allocsPerRun != expectedAllocations {
		t.Errorf("Expected %v allocations, got %v ", expectedAllocations, allocsPerRun)
	}
}

func TestAllocationsPerEncodeTypical(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "temp"
	hdata.MinLength = 0
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 5)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 5)
	checkAllocations(t, hid, minNumbers, 5)
	checkAllocations(t, hid, mixNubers, 5)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 5)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 5)
}

func TestAllocationsPerEncodeNoSalt(t *testing.T) {
	hdata := NewData()
	hdata.Salt = ""
	hdata.MinLength = 0
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 5)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 5)
	checkAllocations(t, hid, minNumbers, 5)
	checkAllocations(t, hid, mixNubers, 5)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 5)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 5)
}

func TestAllocationsPerEncodeMinLength(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "temp"
	hdata.MinLength = 10
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 9)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 5)
	checkAllocations(t, hid, minNumbers, 6)
	checkAllocations(t, hid, mixNubers, 5)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 5)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 5)
}

func TestAllocationsPerEncodeMinLengthHigh(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "temp"
	hdata.MinLength = 100
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocations(t, hid, singleNumber, 15)

	// Same length, same number of allocations
	checkAllocations(t, hid, maxNumbers, 12)
	checkAllocations(t, hid, minNumbers, 15)
	checkAllocations(t, hid, mixNubers, 12)

	// Greater length, same number of allocation
	checkAllocations(t, hid, append(maxNumbers, maxNumbers...), 5)
	checkAllocations(t, hid, append(minNumbers, minNumbers...), 12)
	checkAllocations(t, hid, append(mixNubers, mixNubers...), 9)
}

func checkAllocationsDecode(t *testing.T, hid *HashID, values []int64, expectedAllocations float64) {
	encoded, err := hid.EncodeInt64(values)
	if err != nil {
		t.Errorf("Unexpected error encoding test data: %s, %v", err, values)
	}
	allocsPerRun := testing.AllocsPerRun(5, func() {
		_, err := hid.DecodeInt64WithError(encoded)
		if err != nil {
			t.Errorf("Unexpected error decoding test data: %s, %v", err, values)
		}
	})
	if allocsPerRun != expectedAllocations {
		t.Errorf("Expected %v allocations, got %v ", expectedAllocations, allocsPerRun)
	}
}

func TestAllocationsDecodeTypical(t *testing.T) {
	hdata := NewData()
	hdata.Salt = "temp"
	hdata.MinLength = 0
	hid, _ := NewWithData(hdata)

	singleNumber := []int64{42}

	maxNumbers := []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64}
	minNumbers := []int64{0, 0, 0, 0}
	mixNubers := []int64{math.MaxInt64, 0, 1024, math.MaxInt64 / 2}

	checkAllocationsDecode(t, hid, singleNumber, 12)

	// Same length, same number of allocations
	checkAllocationsDecode(t, hid, maxNumbers, 26)
	checkAllocationsDecode(t, hid, minNumbers, 26)
	checkAllocationsDecode(t, hid, mixNubers, 26)

	// Greater length, same number of allocation
	checkAllocationsDecode(t, hid, append(maxNumbers, maxNumbers...), 40)
	checkAllocationsDecode(t, hid, append(minNumbers, minNumbers...), 40)
	checkAllocationsDecode(t, hid, append(mixNubers, mixNubers...), 40)
}
<|endoftext|>


<commit_before>package hcltest

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl/hclsyntax"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/zclconf/go-cty/cty"
)

// MockBody returns a hcl.Body implementation that works in terms of a
// caller-constructed hcl.BodyContent, thus avoiding the need to parse
// a "real" zcl config file to use as input to a test.
func MockBody(content *hcl.BodyContent) hcl.Body {
	return mockBody{content}
}

type mockBody struct {
	C *hcl.BodyContent
}

func (b mockBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
	content, remainI, diags := b.PartialContent(schema)
	remain := remainI.(mockBody)
	for _, attr := range remain.C.Attributes {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Extraneous attribute in mock body",
			Detail:   fmt.Sprintf("Mock body has extraneous attribute %q.", attr.Name),
			Subject:  &attr.NameRange,
		})
	}
	for _, block := range remain.C.Blocks {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Extraneous block in mock body",
			Detail:   fmt.Sprintf("Mock body has extraneous block of type %q.", block.Type),
			Subject:  &block.DefRange,
		})
	}
	return content, diags
}

func (b mockBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
	ret := &hcl.BodyContent{
		Attributes:       map[string]*hcl.Attribute{},
		Blocks:           []*hcl.Block{},
		MissingItemRange: b.C.MissingItemRange,
	}
	remain := &hcl.BodyContent{
		Attributes:       map[string]*hcl.Attribute{},
		Blocks:           []*hcl.Block{},
		MissingItemRange: b.C.MissingItemRange,
	}
	var diags hcl.Diagnostics

	if len(schema.Attributes) != 0 {
		for _, attrS := range schema.Attributes {
			name := attrS.Name
			attr, ok := b.C.Attributes[name]
			if !ok {
				if attrS.Required {
					diags = append(diags, &hcl.Diagnostic{
						Severity: hcl.DiagError,
						Summary:  "Missing required attribute",
						Detail:   fmt.Sprintf("Mock body doesn't have attribute %q", name),
						Subject:  b.C.MissingItemRange.Ptr(),
					})
				}
				continue
			}
			ret.Attributes[name] = attr
		}
	}

	for attrN, attr := range b.C.Attributes {
		if _, ok := ret.Attributes[attrN]; !ok {
			remain.Attributes[attrN] = attr
		}
	}

	wantedBlocks := map[string]hcl.BlockHeaderSchema{}
	for _, blockS := range schema.Blocks {
		wantedBlocks[blockS.Type] = blockS
	}

	for _, block := range b.C.Blocks {
		if blockS, ok := wantedBlocks[block.Type]; ok {
			if len(block.Labels) != len(blockS.LabelNames) {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Wrong number of block labels",
					Detail:   fmt.Sprintf("Block of type %q requires %d labels, but got %d", blockS.Type, len(blockS.LabelNames), len(block.Labels)),
					Subject:  b.C.MissingItemRange.Ptr(),
				})
			}

			ret.Blocks = append(ret.Blocks, block)
		} else {
			remain.Blocks = append(remain.Blocks, block)
		}
	}

	return ret, mockBody{remain}, diags
}

func (b mockBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
	var diags hcl.Diagnostics
	if len(b.C.Blocks) != 0 {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Mock body has blocks",
			Detail:   "Can't use JustAttributes on a mock body with blocks.",
			Subject:  b.C.MissingItemRange.Ptr(),
		})
	}

	return b.C.Attributes, diags
}

func (b mockBody) MissingItemRange() hcl.Range {
	return b.C.MissingItemRange
}

// MockExprLiteral returns a hcl.Expression that evaluates to the given literal
// value.
func MockExprLiteral(val cty.Value) hcl.Expression {
	return mockExprLiteral{val}
}

type mockExprLiteral struct {
	V cty.Value
}

func (e mockExprLiteral) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	return e.V, nil
}

func (e mockExprLiteral) Variables() []hcl.Traversal {
	return nil
}

func (e mockExprLiteral) Range() hcl.Range {
	return hcl.Range{
		Filename: "MockExprLiteral",
	}
}

func (e mockExprLiteral) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.ExprList
func (e mockExprLiteral) ExprList() []hcl.Expression {
	v := e.V
	ty := v.Type()
	if v.IsKnown() && !v.IsNull() && (ty.IsListType() || ty.IsTupleType()) {
		ret := make([]hcl.Expression, 0, v.LengthInt())
		for it := v.ElementIterator(); it.Next(); {
			_, v := it.Element()
			ret = append(ret, MockExprLiteral(v))
		}
		return ret
	}
	return nil
}

// MockExprVariable returns a hcl.Expression that evaluates to the value of
// the variable with the given name.
func MockExprVariable(name string) hcl.Expression {
	return mockExprVariable(name)
}

type mockExprVariable string

func (e mockExprVariable) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	name := string(e)
	for ctx != nil {
		if val, ok := ctx.Variables[name]; ok {
			return val, nil
		}
		ctx = ctx.Parent()
	}

	// If we fall out here then there is no variable with the given name
	return cty.DynamicVal, hcl.Diagnostics{
		{
			Severity: hcl.DiagError,
			Summary:  "Reference to undefined variable",
			Detail:   fmt.Sprintf("Variable %q is not defined.", name),
		},
	}
}

func (e mockExprVariable) Variables() []hcl.Traversal {
	return []hcl.Traversal{
		{
			hcl.TraverseRoot{
				Name:     string(e),
				SrcRange: e.Range(),
			},
		},
	}
}

func (e mockExprVariable) Range() hcl.Range {
	return hcl.Range{
		Filename: "MockExprVariable",
	}
}

func (e mockExprVariable) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.AbsTraversalForExpr and hcl.RelTraversalForExpr.
func (e mockExprVariable) AsTraversal() hcl.Traversal {
	return hcl.Traversal{
		hcl.TraverseRoot{
			Name:     string(e),
			SrcRange: e.Range(),
		},
	}
}

// MockExprTraversal returns a hcl.Expression that evaluates the given
// absolute traversal.
func MockExprTraversal(traversal hcl.Traversal) hcl.Expression {
	return mockExprTraversal{
		Traversal: traversal,
	}
}

// MockExprTraversalSrc is like MockExprTraversal except it takes a
// traversal string as defined by the native syntax and parses it first.
//
// This method is primarily for testing with hard-coded traversal strings, so
// it will panic if the given string is not syntactically correct.
func MockExprTraversalSrc(src string) hcl.Expression {
	traversal, diags := hclsyntax.ParseTraversalAbs([]byte(src), "MockExprTraversal", hcl.Pos{})
	if diags.HasErrors() {
		panic("invalid traversal string")
	}
	return MockExprTraversal(traversal)
}

type mockExprTraversal struct {
	Traversal hcl.Traversal
}

func (e mockExprTraversal) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	return e.Traversal.TraverseAbs(ctx)
}

func (e mockExprTraversal) Variables() []hcl.Traversal {
	return []hcl.Traversal{e.Traversal}
}

func (e mockExprTraversal) Range() hcl.Range {
	return e.Traversal.SourceRange()
}

func (e mockExprTraversal) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.AbsTraversalForExpr and hcl.RelTraversalForExpr.
func (e mockExprTraversal) AsTraversal() hcl.Traversal {
	return e.Traversal
}

func MockExprList(exprs []hcl.Expression) hcl.Expression {
	return mockExprList{
		Exprs: exprs,
	}
}

type mockExprList struct {
	Exprs []hcl.Expression
}

func (e mockExprList) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	if len(e.Exprs) == 0 {
		return cty.ListValEmpty(cty.DynamicPseudoType), nil
	}
	vals := make([]cty.Value, 0, len(e.Exprs))
	var diags hcl.Diagnostics

	for _, expr := range e.Exprs {
		val, valDiags := expr.Value(ctx)
		diags = append(diags, valDiags...)
		vals = append(vals, val)
	}

	return cty.ListVal(vals), diags
}

func (e mockExprList) Variables() []hcl.Traversal {
	var traversals []hcl.Traversal
	for _, expr := range e.Exprs {
		traversals = append(traversals, expr.Variables()...)
	}
	return traversals
}

func (e mockExprList) Range() hcl.Range {
	return hcl.Range{
		Filename: "MockExprList",
	}
}

func (e mockExprList) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.ExprList
func (e mockExprList) ExprList() []hcl.Expression {
	return e.Exprs
}

// MockAttrs constructs and returns a hcl.Attributes map with attributes
// derived from the given expression map.
//
// Each entry in the map becomes an attribute whose name is the key and
// whose expression is the value.
func MockAttrs(exprs map[string]hcl.Expression) hcl.Attributes {
	ret := make(hcl.Attributes)
	for name, expr := range exprs {
		ret[name] = &hcl.Attribute{
			Name: name,
			Expr: expr,
			Range: hcl.Range{
				Filename: "MockAttrs",
			},
			NameRange: hcl.Range{
				Filename: "MockAttrs",
			},
		}
	}
	return ret
}
<commit_msg>hcltest: update a remaining stale reference to "zcl"<commit_after>package hcltest

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl/hclsyntax"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/zclconf/go-cty/cty"
)

// MockBody returns a hcl.Body implementation that works in terms of a
// caller-constructed hcl.BodyContent, thus avoiding the need to parse
// a "real" HCL config file to use as input to a test.
func MockBody(content *hcl.BodyContent) hcl.Body {
	return mockBody{content}
}

type mockBody struct {
	C *hcl.BodyContent
}

func (b mockBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
	content, remainI, diags := b.PartialContent(schema)
	remain := remainI.(mockBody)
	for _, attr := range remain.C.Attributes {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Extraneous attribute in mock body",
			Detail:   fmt.Sprintf("Mock body has extraneous attribute %q.", attr.Name),
			Subject:  &attr.NameRange,
		})
	}
	for _, block := range remain.C.Blocks {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Extraneous block in mock body",
			Detail:   fmt.Sprintf("Mock body has extraneous block of type %q.", block.Type),
			Subject:  &block.DefRange,
		})
	}
	return content, diags
}

func (b mockBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
	ret := &hcl.BodyContent{
		Attributes:       map[string]*hcl.Attribute{},
		Blocks:           []*hcl.Block{},
		MissingItemRange: b.C.MissingItemRange,
	}
	remain := &hcl.BodyContent{
		Attributes:       map[string]*hcl.Attribute{},
		Blocks:           []*hcl.Block{},
		MissingItemRange: b.C.MissingItemRange,
	}
	var diags hcl.Diagnostics

	if len(schema.Attributes) != 0 {
		for _, attrS := range schema.Attributes {
			name := attrS.Name
			attr, ok := b.C.Attributes[name]
			if !ok {
				if attrS.Required {
					diags = append(diags, &hcl.Diagnostic{
						Severity: hcl.DiagError,
						Summary:  "Missing required attribute",
						Detail:   fmt.Sprintf("Mock body doesn't have attribute %q", name),
						Subject:  b.C.MissingItemRange.Ptr(),
					})
				}
				continue
			}
			ret.Attributes[name] = attr
		}
	}

	for attrN, attr := range b.C.Attributes {
		if _, ok := ret.Attributes[attrN]; !ok {
			remain.Attributes[attrN] = attr
		}
	}

	wantedBlocks := map[string]hcl.BlockHeaderSchema{}
	for _, blockS := range schema.Blocks {
		wantedBlocks[blockS.Type] = blockS
	}

	for _, block := range b.C.Blocks {
		if blockS, ok := wantedBlocks[block.Type]; ok {
			if len(block.Labels) != len(blockS.LabelNames) {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Wrong number of block labels",
					Detail:   fmt.Sprintf("Block of type %q requires %d labels, but got %d", blockS.Type, len(blockS.LabelNames), len(block.Labels)),
					Subject:  b.C.MissingItemRange.Ptr(),
				})
			}

			ret.Blocks = append(ret.Blocks, block)
		} else {
			remain.Blocks = append(remain.Blocks, block)
		}
	}

	return ret, mockBody{remain}, diags
}

func (b mockBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
	var diags hcl.Diagnostics
	if len(b.C.Blocks) != 0 {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Mock body has blocks",
			Detail:   "Can't use JustAttributes on a mock body with blocks.",
			Subject:  b.C.MissingItemRange.Ptr(),
		})
	}

	return b.C.Attributes, diags
}

func (b mockBody) MissingItemRange() hcl.Range {
	return b.C.MissingItemRange
}

// MockExprLiteral returns a hcl.Expression that evaluates to the given literal
// value.
func MockExprLiteral(val cty.Value) hcl.Expression {
	return mockExprLiteral{val}
}

type mockExprLiteral struct {
	V cty.Value
}

func (e mockExprLiteral) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	return e.V, nil
}

func (e mockExprLiteral) Variables() []hcl.Traversal {
	return nil
}

func (e mockExprLiteral) Range() hcl.Range {
	return hcl.Range{
		Filename: "MockExprLiteral",
	}
}

func (e mockExprLiteral) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.ExprList
func (e mockExprLiteral) ExprList() []hcl.Expression {
	v := e.V
	ty := v.Type()
	if v.IsKnown() && !v.IsNull() && (ty.IsListType() || ty.IsTupleType()) {
		ret := make([]hcl.Expression, 0, v.LengthInt())
		for it := v.ElementIterator(); it.Next(); {
			_, v := it.Element()
			ret = append(ret, MockExprLiteral(v))
		}
		return ret
	}
	return nil
}

// MockExprVariable returns a hcl.Expression that evaluates to the value of
// the variable with the given name.
func MockExprVariable(name string) hcl.Expression {
	return mockExprVariable(name)
}

type mockExprVariable string

func (e mockExprVariable) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	name := string(e)
	for ctx != nil {
		if val, ok := ctx.Variables[name]; ok {
			return val, nil
		}
		ctx = ctx.Parent()
	}

	// If we fall out here then there is no variable with the given name
	return cty.DynamicVal, hcl.Diagnostics{
		{
			Severity: hcl.DiagError,
			Summary:  "Reference to undefined variable",
			Detail:   fmt.Sprintf("Variable %q is not defined.", name),
		},
	}
}

func (e mockExprVariable) Variables() []hcl.Traversal {
	return []hcl.Traversal{
		{
			hcl.TraverseRoot{
				Name:     string(e),
				SrcRange: e.Range(),
			},
		},
	}
}

func (e mockExprVariable) Range() hcl.Range {
	return hcl.Range{
		Filename: "MockExprVariable",
	}
}

func (e mockExprVariable) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.AbsTraversalForExpr and hcl.RelTraversalForExpr.
func (e mockExprVariable) AsTraversal() hcl.Traversal {
	return hcl.Traversal{
		hcl.TraverseRoot{
			Name:     string(e),
			SrcRange: e.Range(),
		},
	}
}

// MockExprTraversal returns a hcl.Expression that evaluates the given
// absolute traversal.
func MockExprTraversal(traversal hcl.Traversal) hcl.Expression {
	return mockExprTraversal{
		Traversal: traversal,
	}
}

// MockExprTraversalSrc is like MockExprTraversal except it takes a
// traversal string as defined by the native syntax and parses it first.
//
// This method is primarily for testing with hard-coded traversal strings, so
// it will panic if the given string is not syntactically correct.
func MockExprTraversalSrc(src string) hcl.Expression {
	traversal, diags := hclsyntax.ParseTraversalAbs([]byte(src), "MockExprTraversal", hcl.Pos{})
	if diags.HasErrors() {
		panic("invalid traversal string")
	}
	return MockExprTraversal(traversal)
}

type mockExprTraversal struct {
	Traversal hcl.Traversal
}

func (e mockExprTraversal) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	return e.Traversal.TraverseAbs(ctx)
}

func (e mockExprTraversal) Variables() []hcl.Traversal {
	return []hcl.Traversal{e.Traversal}
}

func (e mockExprTraversal) Range() hcl.Range {
	return e.Traversal.SourceRange()
}

func (e mockExprTraversal) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.AbsTraversalForExpr and hcl.RelTraversalForExpr.
func (e mockExprTraversal) AsTraversal() hcl.Traversal {
	return e.Traversal
}

func MockExprList(exprs []hcl.Expression) hcl.Expression {
	return mockExprList{
		Exprs: exprs,
	}
}

type mockExprList struct {
	Exprs []hcl.Expression
}

func (e mockExprList) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
	if len(e.Exprs) == 0 {
		return cty.ListValEmpty(cty.DynamicPseudoType), nil
	}
	vals := make([]cty.Value, 0, len(e.Exprs))
	var diags hcl.Diagnostics

	for _, expr := range e.Exprs {
		val, valDiags := expr.Value(ctx)
		diags = append(diags, valDiags...)
		vals = append(vals, val)
	}

	return cty.ListVal(vals), diags
}

func (e mockExprList) Variables() []hcl.Traversal {
	var traversals []hcl.Traversal
	for _, expr := range e.Exprs {
		traversals = append(traversals, expr.Variables()...)
	}
	return traversals
}

func (e mockExprList) Range() hcl.Range {
	return hcl.Range{
		Filename: "MockExprList",
	}
}

func (e mockExprList) StartRange() hcl.Range {
	return e.Range()
}

// Implementation for hcl.ExprList
func (e mockExprList) ExprList() []hcl.Expression {
	return e.Exprs
}

// MockAttrs constructs and returns a hcl.Attributes map with attributes
// derived from the given expression map.
//
// Each entry in the map becomes an attribute whose name is the key and
// whose expression is the value.
func MockAttrs(exprs map[string]hcl.Expression) hcl.Attributes {
	ret := make(hcl.Attributes)
	for name, expr := range exprs {
		ret[name] = &hcl.Attribute{
			Name: name,
			Expr: expr,
			Range: hcl.Range{
				Filename: "MockAttrs",
			},
			NameRange: hcl.Range{
				Filename: "MockAttrs",
			},
		}
	}
	return ret
}
<|endoftext|>


<commit_before>package dsbldr

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

// FeatureList is a custom flag type
type FeatureList []string

// String return value of custom flag type
func (fl *FeatureList) String() string {
	return fmt.Sprint(*fl)
}

// Set value of custom flag type
func (fl *FeatureList) Set(value string) error {
	features := strings.Split(value, " ")
	for _, i := range features {
		*fl = append(*fl, i)
	}
	return nil
}

var featureList FeatureList

func init() {
	flag.Var(
		&featureList,
		"features",
		"comma-separated list of features. Defaults to all features",
	)
}

// Builder is main type for this tool.
type Builder struct {
	BaseURL        string
	RequestHeaders map[string]string // Custom Request Headers including auth
	featureMap     map[string]*Feature
	data           [][]string // Strings of Data to be read in to CSV
	saveFeatures   map[string]bool
}

// NewBuilder creates new Builder struct
func NewBuilder(featureCount, recordCount int) *Builder {
	// Add extra row for header
	preallocatedData := make([][]string, recordCount+1)
	for i := range preallocatedData {
		preallocatedData[i] = make([]string, featureCount)
	}
	return &Builder{
		RequestHeaders: make(map[string]string),
		featureMap:     make(map[string]*Feature),
		data:           preallocatedData,
	}
}

func (b *Builder) addDataFeature(featureName string, values []string) error {
	writeStringColumn(&b.data, featureName, values)
	return nil
}

func (b *Builder) createClientAndRequest(
	endpoint string,
	headers map[string]string,
) (*http.Client, *http.Request, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", b.BaseURL+endpoint, nil)
	for k, v := range headers {
		req.Header.Add(k, v)
	}
	return client, req, err
}

// Run Builder to aggregate all features and manage concurrent operations
func (b *Builder) Run() error {
	flag.Parse()

	// Set noSave to true for any features listed in the call
	for _, i := range ignoredFeatures {
		b.GetFeature(i).noSave = true
	}

	for _, feature := range b.featureMap {
		go func(){
			// Run operations concurrently here
			// (use channels representing download the status
			// of each record to get values)
			// Requests should be cached since several requests
			// will have many duplicates (as some will share the same response)

			// TODO:
				// - Figure out caching stuff within createClientAndRequest
				// -- (Maybe just do the caching manually)
				// - Use channels to make certain download operations
				//   wait till the dependent values are loaded
		}()
		client, req, err := b.createClientAndRequest(
			feature.Endpoint, b.RequestHeaders)
		if err != nil {
			return err
		}
		resp, err := client.Do(req)
		parsedResponse, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		output := feature.RunFunc(string(parsedResponse))
		b.addDataFeature(feature.Name, output)
	}
	return nil
}

// Save commits the downloaded features to a file
// as specified by the Writer interface (has to implement WriteAll)
func (b *Builder) Save(writer io.Writer) error {
	for _, i := range b.data {
		var record []string
		for index, j := range b.data[i] {
			// if data header (feature name) has noSave == false, else don't write
			if !b.featureMap[b.data[0][index]].noSave {
				// find out if Golang append acts similarly to lists in pytohn
				// if not, implement special version in util.go
				record = append(record, j)
			}
		}
		err := writer.Write(record)
		if err != nil {
			return err
		}
	}
	return nil
}

// AddFeatures adds a Feature struct to the "Features" Field on Builder
func (b *Builder) AddFeatures(features ...*Feature) {
	for _, feature := range features {
		feature.noSave = false
		b.featureMap[feature.Name] = feature
		b.saveFeatures[feature.Name] = true
	}
	// Increase size of data if feature map is larger than initially allocated
	if len(b.featureMap) > len(b.data[0]) {
		for i := range b.data {
			b.data[i] = append(b.data[i], "")
		}
	}
}

// GetFeature returns a feature in the detaset based on it's name
func (b *Builder) GetFeature(name string) *Feature {
	var feat *Feature
	if val, ok := b.featureMap[name]; ok {
		feat = val
		return feat
	}
	return feat
}
<commit_msg>Implement Builder.Run and subordinate functions (retrieveFeature, resolveFeatureEndpoints, createRequest, getDataFeature)<commit_after>package dsbldr

import (
	"encoding/csv"
	"io/ioutil"
	"net/http"
)

// Builder is main type for this tool.
type Builder struct {
	BaseURL        string
	RequestHeaders map[string]string // Custom Request Headers including auth
	featureMap     map[string]*Feature
	data           [][]string // Strings of Data to be read in to CSV
	records        int        // Number of records to be retrieved for dataset
}

// NewBuilder creates new Builder struct
func NewBuilder(featureCount, recordCount int) *Builder {
	// Add extra row for header
	preallocatedData := make([][]string, recordCount+1)
	for i := range preallocatedData {
		preallocatedData[i] = make([]string, featureCount)
	}
	return &Builder{
		RequestHeaders: make(map[string]string),
		featureMap:     make(map[string]*Feature),
		data:           preallocatedData,
		records:        recordCount,
	}
}

func (b *Builder) addDataFeature(featureName string, values []string) error {
	writeStringColumn(&b.data, featureName, values)
	return nil
}

func (b *Builder) getDataFeature(featureName string) []string {
	items := make([]string, b.records-1) // all items excluding header row
	var colIndex int

	for i := range b.data[0] {
		// Find first column with same header
		if b.data[0][i] == featureName {
			colIndex = i
			break
		}
	}

	// Add all the values as well (remember that Builder.data is pre-allocated)
	for i := 1; i < len(b.data); i++ {
		items[i-1] = b.data[i][colIndex]
	}

	return items
}

// GetFeature returns a feature in the detaset based on it's name
func (b *Builder) GetFeature(name string) *Feature {
	var feat *Feature
	if val, ok := b.featureMap[name]; ok {
		feat = val
		return feat
	}
	return nil
}

// Save commits the downloaded features to a file
func (b *Builder) Save(writer csv.Writer) error {
	for i := range b.data {
		var record []string
		for index, j := range b.data[i] {
			// if data header (feature name) has noSave == false, else don't write
			if !b.featureMap[b.data[0][index]].noSave {
				record = append(record, j)
			}
		}
		err := writer.Write(record)
		if err != nil {
			return err
		}
	}
	return nil
}

// AddFeatures adds a Feature struct to the "Features" Field on Builder
func (b *Builder) AddFeatures(features ...*Feature) {
	for _, feature := range features {
		feature.noSave = false
		b.featureMap[feature.Name] = feature
		// TODO: Return error if feature with same name has been added
	}
	// Increase size of data matrix if feature map is larger than initially allocated
	if len(b.featureMap) > len(b.data[0]) {
		for i := range b.data {
			b.data[i] = append(b.data[i], "")
		}
	}
}

func (b *Builder) createRequest(
	endpoint string,
	headers map[string]string,
) (*http.Request, error) {

	req, err := http.NewRequest("GET", b.BaseURL+endpoint, nil)
	if err != nil {
		return nil, err
	}

	for k, v := range headers {
		req.Header.Add(k, v)
	}
	return req, nil
}

// resolve endpoint templates to actual values for endpoints based on parent features
func (b *Builder) resolveFeatureEndpoints(feature *Feature) ([]string, error) {
	endpoints := make([]string, b.records)
	parents, err := feature.getParentNames()
	if err != nil {
		return nil, err
	}

	for i := 0; i < b.records; i++ {
		parentValuesMap := make(map[string]string)
		for _, j := range parents {
			parentValuesMap[j] = b.getDataFeature(j)[i]
		}

	}

	return endpoints, nil
}

// retrieveFeature returns string dumps of responses and an error if any
func (b *Builder) retrieveFeature(feature *Feature) ([]string, error) {
	client := &http.Client{}
	responses := make([]*http.Response, b.records)
	responseDumps := make([]string, b.records)
	endpoints, err := b.resolveFeatureEndpoints(feature)
	if err != nil {
		return nil, err
	}

	for i := 0; i < b.records; i++ {
		// endpoints should be the same as the number of
		req, err := b.createRequest(endpoints[i], b.RequestHeaders)
		if err != nil {
			return nil, err
		}

		resp, err := client.Do(req)
		if err != nil {
			return nil, err
		}

		defer resp.Body.Close()

		if resp.StatusCode == 200 { // OK
			bodyBytes, _ := ioutil.ReadAll(resp.Body)
			responseDumps[i] = string(bodyBytes)
		}
	}

	return responseDumps, nil
}

// Run Builder to aggregate all features and manage concurrent operations
func (b *Builder) Run() error {
	// flag.Parse()

	// Set noSave to true for any features listed in the call
	// for _, i := range ignoredFeatures {
	// 	b.GetFeature(i).noSave = true
	// }

	for _, feature := range b.featureMap {
		parents, err := feature.getParentNames()
		if err != nil {
			return err
		}

		var retrieveError error
		go func(feature *Feature) {
			for _, i := range parents {
				// Loop through parent features and wait till
				// they finish excecuting
				<-b.GetFeature(i).finished
			}

			parsedResponses := make([]string, b.records)
			parsedResponses, retrieveError = b.retrieveFeature(feature)

			output := feature.RunFunc(parsedResponses)
			b.addDataFeature(feature.Name, output)

			feature.finished <- true // Mark feature as true
		}(feature)

		if retrieveError != nil {
			return retrieveError
		}

	}
	return nil
}
<|endoftext|>