<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/nranchev/go-libGeoIP"
	"github.com/streadway/amqp"
	"io"
	"koding/kontrol/kontrolproxy/proxyconfig"
	"koding/tools/config"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func init() {
	log.SetPrefix("kontrol-proxy ")
}

type IncomingMessage struct {
	ProxyResponse *proxyconfig.ProxyResponse
}

type RabbitChannel struct {
	ReplyTo string
	Receive chan []byte
}

var proxy proxyconfig.Proxy // this will be only updated whenever we receive a msg from kontrold
var proxyDB *proxyconfig.ProxyConfiguration
var amqpStream *AmqpStream
var start chan bool
var first bool = true
var connections map[string]RabbitChannel
var geoIP *libgeo.GeoIP

func main() {
	log.Printf("kontrol proxy started ")
	start = make(chan bool)
	connections = make(map[string]RabbitChannel)

	// open kontrol-daemon database connection
	var err error
	proxyDB, err = proxyconfig.Connect()
	if err != nil {
		log.Fatalf("proxyconfig mongodb connect: %s", err)
	}

	// load GeoIP db into memory
	dbFile := "GeoIP.dat"
	geoIP, err = libgeo.Load(dbFile)
	if err != nil {
		log.Printf("load GeoIP.dat: %s\n", err.Error())
	}

	// register proxy instance to kontrol-daemon
	amqpStream = setupAmqp()

	// declare rabbitproxy exchange. this is used by rabbitclients to create
	// artifical host in form {name}-{key}-{username}.x.koding.com and use it
	err = amqpStream.channel.ExchangeDeclare("kontrol-rabbitproxy", "direct", true, false, false, false, nil)
	if err != nil {
		log.Printf("exchange.declare: %s\n", err.Error())
	}

	log.Printf("register proxy to kontrold with uuid '%s'", amqpStream.uuid)
	amqpStream.Publish("infoExchange", "input.proxy", buildProxyCmd("addProxy", amqpStream.uuid))
	log.Println("register command is send. waiting for response from kontrold...")
	go handleInput(amqpStream.input, amqpStream.uuid)

	<-start // wait until we got message from kontrold or exit via above chan

	reverseProxy := &ReverseProxy{}
	// http.HandleFunc("/", reverseProxy.ServeHTTP) this works for 1.1
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		reverseProxy.ServeHTTP(w, r)
	})

	port := strconv.Itoa(config.Current.Kontrold.Proxy.Port)
	portssl := strconv.Itoa(config.Current.Kontrold.Proxy.PortSSL)

	go func() {
		err = http.ListenAndServeTLS(":"+portssl, "cert.pem", "key.pem", nil)
		if err != nil {
			log.Println("https mode is disabled. please add cert.pem and key.pem files.")
		} else {
			log.Printf("https mode is enabled. serving at :%s ...", portssl)
		}
	}()

	log.Printf("normal mode is enabled. serving at :%s ...", port)
	err = http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Println(err)
	}
}

func handleInput(input <-chan amqp.Delivery, uuid string) {
	for d := range input {
		var msg IncomingMessage
		err := json.Unmarshal(d.Body, &msg)
		if err != nil {
			log.Print("bad json incoming msg: ", err)
		}

		if msg.ProxyResponse != nil {
			if msg.ProxyResponse.Action == "updateProxy" {
				log.Println("update action received from kontrold. updating proxy route table")
				var err error
				proxy, err = proxyDB.GetProxy(uuid)
				if err != nil {
					log.Println(err)
				}

				if first {
					start <- true
					first = false
					log.Println("routing tables updated. ready to start servers.")
				}
			}
		} else {
			log.Println("incoming message is in wrong format")
		}
	}
}

/*************************************************
*
*  util functions
*
*  - arslan
*************************************************/

func buildProxyCmd(action, uuid string) []byte {
	var req proxyconfig.ProxyMessage
	req.Action = action
	req.Uuid = uuid

	data, err := json.Marshal(req)
	if err != nil {
		log.Println("json marshall error", err)
	}

	return data
}

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Given a string of the form "host", "port", returns "host:port"
func addPort(host, port string) string {
	if ok := hasPort(host); ok {
		return host
	}

	return host + ":" + port
}

// Check if a server is alive or not
func checkServer(host string) error {
	c, err := net.Dial("tcp", host)
	if err != nil {
		return err
	}
	c.Close()
	return nil
}

/*************************************************
*
*  modified version of go's reverseProxy source code
*  has support for dynamic target url, websockets and amqp
*
*  - arslan
*************************************************/

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te", // canonicalized version of "TE"
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",
}

func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	outreq := new(http.Request)
	*outreq = *req // includes shallow copies of maps, but okay

	websocket := checkWebsocket(outreq)

	userInfo, err := populateUser(outreq)
	if err != nil {
		io.WriteString(rw, fmt.Sprintf("{\"err\":\"%s\"}\n", err.Error()))
		log.Printf("error parsing subdomain %s: %v", outreq.Host, err)
		return
	}
	fmt.Printf("--\nconnected user information %v\n", userInfo)

	result, ok := validateUser(userInfo)
	if !ok {
		http.NotFound(rw, req)
		return
	}
	fmt.Printf("validation result: %s\n", result)

	// either userInfo.FullUrl or userInfo.Servicename-Key lookup will be made
	target, err := targetHost(userInfo)
	if err != nil {
		if err.Error() == "redirect" {
			http.Redirect(rw, req, target.String(), http.StatusTemporaryRedirect)
			return
		}
		log.Printf("error running key proxy %s: %v", userInfo.FullUrl, err)
		io.WriteString(rw, fmt.Sprintf("{\"err\":\"%s\"}\n", err.Error()))
		return
	}
	fmt.Printf("proxy to %s\n", target.Host)

	// Reverseproxy.Director closure
	targetQuery := target.RawQuery
	outreq.URL.Scheme = target.Scheme
	outreq.URL.Host = target.Host
	outreq.URL.Path = singleJoiningSlash(target.Path, outreq.URL.Path)
	if targetQuery == "" || outreq.URL.RawQuery == "" {
		outreq.URL.RawQuery = targetQuery + outreq.URL.RawQuery
	} else {
		outreq.URL.RawQuery = targetQuery + "&" + outreq.URL.RawQuery
	}

	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	// https://groups.google.com/d/msg/golang-nuts/KBx9pDlvFOc/edt4iad96nwJ
	if websocket {
		fmt.Println("connection via websocket")
		rConn, err := net.Dial("tcp", outreq.URL.Host)
		if err != nil {
			http.Error(rw, "Error contacting backend server.", http.StatusInternalServerError)
			log.Printf("Error dialing websocket backend %s: %v", outreq.URL.Host, err)
			return
		}

		hj, ok := rw.(http.Hijacker)
		if !ok {
			http.Error(rw, "Not a hijacker?", http.StatusInternalServerError)
			return
		}

		conn, _, err := hj.Hijack()
		if err != nil {
			log.Printf("Hijack error: %v", err)
			return
		}
		defer conn.Close()
		defer rConn.Close()

		err = req.Write(rConn)
		if err != nil {
			log.Printf("Error copying request to target: %v", err)
			return
		}

		go p.copyResponse(rConn, conn)
		p.copyResponse(conn, rConn)

	} else {
		transport := p.Transport
		if transport == nil {
			transport = http.DefaultTransport
		}

		// Display error when someone hits the main page
		hostname, _ := os.Hostname()
		if hostname == outreq.URL.Host {
			io.WriteString(rw, "{\"err\":\"no such host\"}\n")
			return
		}

		// Remove hop-by-hop headers to the backend.  Especially
		// important is "Connection" because we want a persistent
		// connection, regardless of what the client sent to us.  This
		// is modifying the same underlying map from req (shallow
		// copied above) so we only copy it if necessary.
		copiedHeaders := false
		for _, h := range hopHeaders {
			if outreq.Header.Get(h) != "" {
				if !copiedHeaders {
					outreq.Header = make(http.Header)
					copyHeader(outreq.Header, req.Header)
					copiedHeaders = true
				}
				outreq.Header.Del(h)
			}
		}

		if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
			// If we aren't the first proxy retain prior
			// X-Forwarded-For information as a comma+space
			// separated list and fold multiple headers into one.
			if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
			outreq.Header.Set("X-Forwarded-For", clientIP)
		}

		res := new(http.Response)
		rabbitKey := lookupRabbitKey(userInfo.Username, userInfo.Servicename, userInfo.Key)

		if rabbitKey != "" {
			fmt.Println("connection via rabbitmq")
			res, err = rabbitTransport(outreq, userInfo, rabbitKey)
			if err != nil {
				log.Printf("rabbit proxy %s", err.Error())
				io.WriteString(rw, fmt.Sprintf("{\"err\":\"%s\"}\n", err.Error()))
				return
			}
		} else {
			fmt.Println("connection via normal http")
			// add :80 if not available
			ok := hasPort(outreq.URL.Host)
			if !ok {
				outreq.URL.Host = addPort(outreq.URL.Host, "80")
			}

			res, err = transport.RoundTrip(outreq)
			if err != nil {
				io.WriteString(rw, fmt.Sprint(err))
				return
			}
		}

		defer res.Body.Close()

		copyHeader(rw.Header(), res.Header)
		rw.WriteHeader(res.StatusCode)
		p.copyResponse(rw, res.Body)
	}

}

func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
	io.Copy(dst, src)
}
<commit_msg>kontrolrproxy: redirect http to https for new.koding.com<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/nranchev/go-libGeoIP"
	"github.com/streadway/amqp"
	"io"
	"koding/kontrol/kontrolproxy/proxyconfig"
	"koding/tools/config"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func init() {
	log.SetPrefix("kontrol-proxy ")
}

type IncomingMessage struct {
	ProxyResponse *proxyconfig.ProxyResponse
}

type RabbitChannel struct {
	ReplyTo string
	Receive chan []byte
}

var proxy proxyconfig.Proxy // this will be only updated whenever we receive a msg from kontrold
var proxyDB *proxyconfig.ProxyConfiguration
var amqpStream *AmqpStream
var start chan bool
var first bool = true
var connections map[string]RabbitChannel
var geoIP *libgeo.GeoIP

func main() {
	log.Printf("kontrol proxy started ")
	start = make(chan bool)
	connections = make(map[string]RabbitChannel)

	// open kontrol-daemon database connection
	var err error
	proxyDB, err = proxyconfig.Connect()
	if err != nil {
		log.Fatalf("proxyconfig mongodb connect: %s", err)
	}

	// load GeoIP db into memory
	dbFile := "GeoIP.dat"
	geoIP, err = libgeo.Load(dbFile)
	if err != nil {
		log.Printf("load GeoIP.dat: %s\n", err.Error())
	}

	// register proxy instance to kontrol-daemon
	amqpStream = setupAmqp()

	// declare rabbitproxy exchange. this is used by rabbitclients to create
	// artifical host in form {name}-{key}-{username}.x.koding.com and use it
	err = amqpStream.channel.ExchangeDeclare("kontrol-rabbitproxy", "direct", true, false, false, false, nil)
	if err != nil {
		log.Printf("exchange.declare: %s\n", err.Error())
	}

	log.Printf("register proxy to kontrold with uuid '%s'", amqpStream.uuid)
	amqpStream.Publish("infoExchange", "input.proxy", buildProxyCmd("addProxy", amqpStream.uuid))
	log.Println("register command is send. waiting for response from kontrold...")
	go handleInput(amqpStream.input, amqpStream.uuid)

	<-start // wait until we got message from kontrold or exit via above chan

	reverseProxy := &ReverseProxy{}
	// http.HandleFunc("/", reverseProxy.ServeHTTP) this works for 1.1
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		reverseProxy.ServeHTTP(w, r)
	})

	port := strconv.Itoa(config.Current.Kontrold.Proxy.Port)
	portssl := strconv.Itoa(config.Current.Kontrold.Proxy.PortSSL)

	go func() {
		err = http.ListenAndServeTLS(":"+portssl, "cert.pem", "key.pem", nil)
		if err != nil {
			log.Println("https mode is disabled. please add cert.pem and key.pem files.")
		} else {
			log.Printf("https mode is enabled. serving at :%s ...", portssl)
		}
	}()

	log.Printf("normal mode is enabled. serving at :%s ...", port)
	err = http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Println(err)
	}
}

func handleInput(input <-chan amqp.Delivery, uuid string) {
	for d := range input {
		var msg IncomingMessage
		err := json.Unmarshal(d.Body, &msg)
		if err != nil {
			log.Print("bad json incoming msg: ", err)
		}

		if msg.ProxyResponse != nil {
			if msg.ProxyResponse.Action == "updateProxy" {
				log.Println("update action received from kontrold. updating proxy route table")
				var err error
				proxy, err = proxyDB.GetProxy(uuid)
				if err != nil {
					log.Println(err)
				}

				if first {
					start <- true
					first = false
					log.Println("routing tables updated. ready to start servers.")
				}
			}
		} else {
			log.Println("incoming message is in wrong format")
		}
	}
}

/*************************************************
*
*  util functions
*
*  - arslan
*************************************************/

func buildProxyCmd(action, uuid string) []byte {
	var req proxyconfig.ProxyMessage
	req.Action = action
	req.Uuid = uuid

	data, err := json.Marshal(req)
	if err != nil {
		log.Println("json marshall error", err)
	}

	return data
}

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Given a string of the form "host", "port", returns "host:port"
func addPort(host, port string) string {
	if ok := hasPort(host); ok {
		return host
	}

	return host + ":" + port
}

// Check if a server is alive or not
func checkServer(host string) error {
	c, err := net.Dial("tcp", host)
	if err != nil {
		return err
	}
	c.Close()
	return nil
}

/*************************************************
*
*  modified version of go's reverseProxy source code
*  has support for dynamic target url, websockets and amqp
*
*  - arslan
*************************************************/

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te", // canonicalized version of "TE"
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",
}

func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if req.TLS == nil && req.Host == "new.koding.com" {
		http.Redirect(rw, req, "https://new.koding.com"+req.RequestURI, http.StatusMovedPermanently)
	}

	outreq := new(http.Request)
	*outreq = *req // includes shallow copies of maps, but okay

	websocket := checkWebsocket(outreq)

	userInfo, err := populateUser(outreq)
	if err != nil {
		io.WriteString(rw, fmt.Sprintf("{\"err\":\"%s\"}\n", err.Error()))
		log.Printf("error parsing subdomain %s: %v", outreq.Host, err)
		return
	}
	fmt.Printf("--\nconnected user information %v\n", userInfo)

	result, ok := validateUser(userInfo)
	if !ok {
		http.NotFound(rw, req)
		return
	}
	fmt.Printf("validation result: %s\n", result)

	// either userInfo.FullUrl or userInfo.Servicename-Key lookup will be made
	target, err := targetHost(userInfo)
	if err != nil {
		if err.Error() == "redirect" {
			http.Redirect(rw, req, target.String(), http.StatusTemporaryRedirect)
			return
		}
		log.Printf("error running key proxy %s: %v", userInfo.FullUrl, err)
		io.WriteString(rw, fmt.Sprintf("{\"err\":\"%s\"}\n", err.Error()))
		return
	}
	fmt.Printf("proxy to %s\n", target.Host)

	// Reverseproxy.Director closure
	targetQuery := target.RawQuery
	outreq.URL.Scheme = target.Scheme
	outreq.URL.Host = target.Host
	outreq.URL.Path = singleJoiningSlash(target.Path, outreq.URL.Path)
	if targetQuery == "" || outreq.URL.RawQuery == "" {
		outreq.URL.RawQuery = targetQuery + outreq.URL.RawQuery
	} else {
		outreq.URL.RawQuery = targetQuery + "&" + outreq.URL.RawQuery
	}

	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	// https://groups.google.com/d/msg/golang-nuts/KBx9pDlvFOc/edt4iad96nwJ
	if websocket {
		fmt.Println("connection via websocket")
		rConn, err := net.Dial("tcp", outreq.URL.Host)
		if err != nil {
			http.Error(rw, "Error contacting backend server.", http.StatusInternalServerError)
			log.Printf("Error dialing websocket backend %s: %v", outreq.URL.Host, err)
			return
		}

		hj, ok := rw.(http.Hijacker)
		if !ok {
			http.Error(rw, "Not a hijacker?", http.StatusInternalServerError)
			return
		}

		conn, _, err := hj.Hijack()
		if err != nil {
			log.Printf("Hijack error: %v", err)
			return
		}
		defer conn.Close()
		defer rConn.Close()

		err = req.Write(rConn)
		if err != nil {
			log.Printf("Error copying request to target: %v", err)
			return
		}

		go p.copyResponse(rConn, conn)
		p.copyResponse(conn, rConn)

	} else {
		transport := p.Transport
		if transport == nil {
			transport = http.DefaultTransport
		}

		// Display error when someone hits the main page
		hostname, _ := os.Hostname()
		if hostname == outreq.URL.Host {
			io.WriteString(rw, "{\"err\":\"no such host\"}\n")
			return
		}

		// Remove hop-by-hop headers to the backend.  Especially
		// important is "Connection" because we want a persistent
		// connection, regardless of what the client sent to us.  This
		// is modifying the same underlying map from req (shallow
		// copied above) so we only copy it if necessary.
		copiedHeaders := false
		for _, h := range hopHeaders {
			if outreq.Header.Get(h) != "" {
				if !copiedHeaders {
					outreq.Header = make(http.Header)
					copyHeader(outreq.Header, req.Header)
					copiedHeaders = true
				}
				outreq.Header.Del(h)
			}
		}

		if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
			// If we aren't the first proxy retain prior
			// X-Forwarded-For information as a comma+space
			// separated list and fold multiple headers into one.
			if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
			outreq.Header.Set("X-Forwarded-For", clientIP)
		}

		res := new(http.Response)
		rabbitKey := lookupRabbitKey(userInfo.Username, userInfo.Servicename, userInfo.Key)

		if rabbitKey != "" {
			fmt.Println("connection via rabbitmq")
			res, err = rabbitTransport(outreq, userInfo, rabbitKey)
			if err != nil {
				log.Printf("rabbit proxy %s", err.Error())
				io.WriteString(rw, fmt.Sprintf("{\"err\":\"%s\"}\n", err.Error()))
				return
			}
		} else {
			fmt.Println("connection via normal http")
			// add :80 if not available
			ok := hasPort(outreq.URL.Host)
			if !ok {
				outreq.URL.Host = addPort(outreq.URL.Host, "80")
			}

			res, err = transport.RoundTrip(outreq)
			if err != nil {
				io.WriteString(rw, fmt.Sprint(err))
				return
			}
		}

		defer res.Body.Close()

		copyHeader(rw.Header(), res.Header)
		rw.WriteHeader(res.StatusCode)
		p.copyResponse(rw, res.Body)
	}

}

func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
	io.Copy(dst, src)
}
<|endoftext|>


<commit_before>package manager

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"

	"github.com/koding/worker"
	"github.com/streadway/amqp"
)

func New() *Manager {
	return &Manager{}
}

type Manager struct {
	routes          map[string]*Marshaler
	controller      worker.ErrHandler
	controllerValue reflect.Value
}

func (c *Manager) Controller(controller worker.ErrHandler) {
	c.controller = controller
	c.controllerValue = reflect.ValueOf(controller)
}

func (b *Manager) HandleFunc(funcName string, handler interface{}) error {
	if b.routes == nil {
		b.routes = make(map[string]*Marshaler)
	}

	b.routes[funcName] = Marshaled(handler)
	return nil
}

func (b *Manager) HandleEvent(eventName string, data []byte) error {
	route, ok := b.routes[eventName]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return route.HandleEvent(b.controllerValue, data)
}

func (m *Manager) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	return m.controller.DefaultErrHandler(delivery, err)
}

type Marshaler struct {
	v reflect.Value
}

func Marshaled(i interface{}) *Marshaler {
	t := reflect.TypeOf(i)
	if t.Kind() != reflect.Func {
		panic(NewMarshalerError("kind was %v, not Func", t.Kind()))
	}

	// check input parameter count
	if t.NumIn() != 2 {
		panic(NewMarshalerError(
			"input arity was %v, not 2",
			t.NumIn(),
		))
	}

	// check output parameter count
	if t.NumOut() != 1 {
		panic(NewMarshalerError("output arity was %v, not 1", t.NumOut()))
	}

	// output should be errors
	if t.Out(0).String() != "error" {
		panic(NewMarshalerError(
			"type of return value was %v, not error",
			t.Out(0),
		))
	}
	return &Marshaler{reflect.ValueOf(i)}
}

var nilParameter = reflect.ValueOf((*interface{})(nil))

func (m *Marshaler) HandleEvent(controllerValue reflect.Value, data []byte) error {
	var parameter reflect.Value

	if m.v.Type().NumIn() == 2 {
		in2 := m.v.Type().In(1)
		// if incoming paramter is an empty interface
		if reflect.Interface == in2.Kind() && in2.NumMethod() == 0 {
			parameter = nilParameter
			// if incoming parameter is a slice or a map
		} else if reflect.Slice == in2.Kind() || reflect.Map == in2.Kind() {
			// non-pointer maps/slices require special treatment because
			// json.Unmarshal won't work on a non-pointer destination. We
			// add a level indirection here, then deref it before .Call()
			parameter = reflect.New(in2)
		} else {
			// if it is a struct
			parameter = reflect.New(in2.Elem())
		}
	} else {
		// if handler doesnt have any incoming paramters
		parameter = nilParameter
	}

	// this is where magic happens:)
	// first read incoming []byte data into a io.reader then
	// put this data into a decoder(create a decoder out of it)
	// finally decode this data into given
	decoder := reflect.ValueOf(json.NewDecoder(bytes.NewReader(data)))
	res := decoder.MethodByName("Decode").Call([]reflect.Value{parameter})
	if len(res) > 0 && !res[0].IsNil() {
		return res[0].Interface().(error)
	}

	if reflect.Slice == parameter.Elem().Kind() || reflect.Map == parameter.Elem().Kind() {
		parameter = parameter.Elem()
	}

	var out []reflect.Value
	switch m.v.Type().NumIn() {
	case 2:
		out = m.v.Call([]reflect.Value{
			controllerValue,
			parameter,
		})
	default:
		return fmt.Errorf("unknown signature %s", m.v.Type())
	}

	if len(out) > 0 && !out[0].IsNil() {
		return out[0].Interface().(error)
	}

	return nil
}

type MarshalerError string

func NewMarshalerError(format string, args ...interface{}) MarshalerError {
	return MarshalerError(fmt.Sprintf(format, args...))
}

func (e MarshalerError) Error() string { return string(e) }
<commit_msg>Social: remove manager package<commit_after><|endoftext|>


<commit_before>package models

import "encoding/xml"

// ItemSet corresponds to sitemap parent urlset element.
type ItemSet struct {
	XMLName     xml.Name          `xml:"http://www.sitemaps.org/schemas/sitemap/0.9 urlset,name"`
	Definitions []*ItemDefinition `xml:"url"`
}

func NewItemSet() *ItemSet {
	return &ItemSet{
		Definitions: make([]*ItemDefinition, 0),
	}
}

func (s *ItemSet) Populate(c *ItemContainer) {
	s.addItems(c)
	s.deleteItems(c)
	s.updateItems(c)
}

func (s *ItemSet) addItems(c *ItemContainer) {
	for _, v := range c.Add {
		s.Definitions = append(s.Definitions, v)
	}
}

func (s *ItemSet) deleteItems(c *ItemContainer) {

}

func (s *ItemSet) updateItems(c *ItemContainer) {

}
<commit_msg>Sitemap: Handle updated and deleted items in ItemSet<commit_after>package models

import (
	"encoding/xml"
	"fmt"
	"time"
)

// ItemSet corresponds to sitemap parent urlset element.
type ItemSet struct {
	XMLName     xml.Name          `xml:"http://www.sitemaps.org/schemas/sitemap/0.9 urlset,name"`
	Definitions []*ItemDefinition `xml:"url"`
}

func NewItemSet() *ItemSet {
	return &ItemSet{
		Definitions: make([]*ItemDefinition, 0),
	}
}

func (s *ItemSet) Populate(c *ItemContainer) {
	s.addItems(c)
	s.deleteItems(c)
	s.updateItems(c)
}

func (s *ItemSet) addItems(c *ItemContainer) {
	for _, v := range c.Add {
		s.Definitions = append(s.Definitions, v)
	}
}

func (s *ItemSet) updateItems(c *ItemContainer) {
	// for preventing iteration of current items for each updated element
	// first we store current item pointers in a map with their unique slugs
	itemMap := make(map[string]*ItemDefinition)
	for _, v := range s.Definitions {
		itemMap[v.Location] = v
	}

	// then while iterating the updated items, we are fetching related item
	// from map and updating it
	for _, v := range c.Update {
		currentDefinition, ok := itemMap[v.Location]
		if !ok {
			// TODO log this
			fmt.Println("item does not exist")
			continue
		}
		currentDefinition.LastModified = time.Now().UTC().Format(time.RFC3339)
	}
}

func (s *ItemSet) deleteItems(c *ItemContainer) {
	itemMap := make(map[string]int)
	// first we store item pointers in a map with their indexes
	for i, v := range s.Definitions {
		itemMap[v.Location] = i
	}

	// then while iterationg the deleted items, we are deleting the related
	// item from existing item definitions
	for _, v := range c.Delete {
		_, ok := itemMap[v.Location]
		if !ok {
			// TODO log this
			fmt.Println("item does not exist")
			continue
		}

		// TODO it looks like it sucks in performance
		removedIndex := itemMap[v.Location]
		temp := s.Definitions
		temp = append(temp[:removedIndex], temp[removedIndex+1:]...)
		s.Definitions = temp
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"time"
)

var instanceList = []string{"i1", "i2", "i3", "i4", "i5"}
var metricList = []string{"m1", "m2", "m3", "m4", "m5"}
var ticktimeSeconds = 10
var sampletime = 11
var numWorkers = 3
var ticker = time.Tick(time.Duration(ticktimeSeconds) * time.Second)
var jobNumber = 1
var jobTime time.Duration = 7
var completedJobs = 0

type request struct {
	instance  string
	metric    string
	callTime  time.Time
	jobNumber int
}

func doWorkers(id int, ch chan *request) {

	fmt.Printf("W%d - START Worker\n", id)

	// Monitor channel for request struct
	for r := range ch {
		time.Sleep(jobTime * time.Second)
		fmt.Printf("W%d - RESPONSE #%d - %s, %s - %s (TOOK %s)\n", id, r.jobNumber, r.instance, r.metric, time.Now().UTC().Format(time.ANSIC), time.Now().UTC().Sub(r.callTime))
		completedJobs++
	}

}

func doSomething(ch chan *request) {

	endTime := time.Now().UTC()

	// Loop forever - Long Running
	for {
		select {
		case now := <-ticker:

			fmt.Printf("\n\n********************** TICK %s *****************************\n\n", now.UTC().Format(time.ANSIC))

			oldendTime := endTime
			endTime = time.Now().UTC()
			startTime := endTime.Add(-(time.Duration(sampletime) * time.Second))

			// CHECK TICKER IF YOU MADE IT BACK IN TIME

			diff := startTime.Sub(oldendTime)
			fmt.Printf("DIFF: %s (oldendTime: %s - starttime %s)\n", diff, startTime.Format(time.ANSIC), oldendTime.Format(time.ANSIC))
			if diff.Seconds() > 1 {
				fmt.Printf("\n\n\n************* ERROR - YOU MISSED SOME TIME - Not enough time to process *****************\n")
				fmt.Printf("    JOBS NOT FINISHED: %d\n", (jobNumber-1)-completedJobs)
				fmt.Printf("    SKIP A TICK TO CATCH UP\n\n")
			} else {
				fmt.Printf("Perfect - You did not miss any time\n")
				fmt.Printf("JOBS NOT FINISHED YET: %d\n", (jobNumber-1)-completedJobs)

				// Interate over
				for _, instance := range instanceList {

					for _, metric := range metricList {

						// BLOCKS - WAITS FOR WORKER
						ch <- &request{
							instance:  instance,
							metric:    metric,
							callTime:  time.Now().UTC(),
							jobNumber: jobNumber,
						}
						fmt.Printf("CALL #%d - %s %s %s\n", jobNumber, instance, metric, time.Now().UTC().Format(time.ANSIC))
						jobNumber++
					}
				}
			}
		}
	}
}

func main() {

	ch := make(chan *request)

	// CREATE WORKERS
	for i := 0; i < numWorkers; i++ {
		go doWorkers(i, ch)
	}

	doSomething(ch)

}
<commit_msg>updated worker<commit_after>package main

import (
	"context"
	"fmt"

	"time"
)

var instanceList = []string{"i1", "i2", "i3", "i4", "i5"}
var metricList = []string{"m1", "m2", "m3", "m4", "m5"}
var ticktimeSeconds = 10
var sampletime = 11
var numWorkers = 3
var ticker = time.Tick(time.Duration(ticktimeSeconds) * time.Second)
var jobNumber = 1
var jobTime time.Duration = 7
var completedJobs = 0

type request struct {
	instance  string
	metric    string
	callTime  time.Time
	jobNumber int
	done      bool
}

// doWorkers is the Worker
func doWorkers(ctx context.Context, id int, ch chan *request) {

	fmt.Printf("W%d - START Worker\n", id)

	// Monitor channel for request struct
	for {
		select {
		// receive
		case r := <-ch:
			time.Sleep(jobTime * time.Second)
			fmt.Printf("W%d - RESPONSE #%d - %s, %s - %s (TOOK %s)\n", id, r.jobNumber, r.instance, r.metric, time.Now().UTC().Format(time.ANSIC), time.Now().UTC().Sub(r.callTime))
			completedJobs++
		// send
		case <-ctx.Done():
			return
		}
	}

}

func doSomething(ch chan *request) {

	endTime := time.Now().UTC()

	// Loop forever - Long Running
	for {
		select {
		case now := <-ticker:

			fmt.Printf("\n\n********************** TICK %s *****************************\n\n", now.UTC().Format(time.ANSIC))

			oldendTime := endTime
			endTime = time.Now().UTC()
			startTime := endTime.Add(-(time.Duration(sampletime) * time.Second))

			// CHECK TICKER IF YOU MADE IT BACK IN TIME

			diff := startTime.Sub(oldendTime)
			fmt.Printf("DIFF: %s (oldendTime: %s - starttime %s)\n", diff, startTime.Format(time.ANSIC), oldendTime.Format(time.ANSIC))
			if diff.Seconds() > 1 {
				fmt.Printf("\n\n\n************* ERROR - YOU MISSED SOME TIME - Not enough time to process *****************\n")
				fmt.Printf("    JOBS NOT FINISHED: %d\n", (jobNumber-1)-completedJobs)
				fmt.Printf("    SKIP A TICK TO CATCH UP\n\n")
			} else {
				fmt.Printf("Perfect - You did not miss any time\n")
				fmt.Printf("JOBS NOT FINISHED YET: %d\n", (jobNumber-1)-completedJobs)

				// Interate over
				for _, instance := range instanceList {

					for _, metric := range metricList {

						// BLOCKS - WAITS FOR WORKER
						ch <- &request{
							instance:  instance,
							metric:    metric,
							callTime:  time.Now().UTC(),
							jobNumber: jobNumber,
							done:      false,
						}
						// Find out when a job is done

						fmt.Printf("CALL #%d - %s %s %s\n", jobNumber, instance, metric, time.Now().UTC().Format(time.ANSIC))
						jobNumber++
					}
				}
			}
		}
	}
}

func main() {

	ch := make(chan *request)
	ctx, cancelFunc := context.WithCancel(context.Background())

	// CREATE WORKERS
	for i := 0; i < numWorkers; i++ {
		go doWorkers(ctx, i, ch)
	}

	doSomething(ch)

	// Will never get to this
	cancelFunc() //cleans up all wokers

}
<|endoftext|>


<commit_before>package specs

import (
	"bufio"
	"testing"
	"text/scanner"

	"github.com/NetSys/quilt/db"
	"github.com/NetSys/quilt/engine"
	"github.com/NetSys/quilt/stitch"
	"github.com/NetSys/quilt/util"
)

func configRunOnce(configPath string, quiltPath []string) error {
	f, err := util.Open(configPath)
	if err != nil {
		return err
	}
	defer f.Close()

	var sc scanner.Scanner
	spec, err := stitch.New(*sc.Init(bufio.NewReader(f)), quiltPath)
	if err != nil {
		return err
	}

	err = engine.UpdatePolicy(db.New(), spec)
	if err != nil {
		return err
	}

	return nil
}

func TestConfigs(t *testing.T) {
	testConfig := func(configPath string, quiltPath []string) {
		if err := configRunOnce(configPath, quiltPath); err != nil {
			t.Errorf("%s failed validation: %s", configPath, err.Error())
		}
	}
	testConfig("example.spec", []string{"specs/stdlib"})
	testConfig("../quilt-tester/config/config.spec", []string{"specs/stdlib"})
	testConfig("./spark/sparkPI.spec",
		[]string{"./stdlib", "./spark", "./zookeeper"})
	testConfig("./wordpress/main.spec",
		[]string{"./stdlib", "./wordpress", "./spark", "./zookeeper"})
}
<commit_msg>specs: Don't run UpdatePolicy in spec_test<commit_after>package specs

import (
	"bufio"
	"testing"
	"text/scanner"

	"github.com/NetSys/quilt/stitch"
	"github.com/NetSys/quilt/util"
)

func configRunOnce(configPath string, quiltPath []string) error {
	f, err := util.Open(configPath)
	if err != nil {
		return err
	}
	defer f.Close()

	var sc scanner.Scanner
	if _, err = stitch.New(*sc.Init(bufio.NewReader(f)), quiltPath); err != nil {
		return err
	}

	return nil
}

func TestConfigs(t *testing.T) {
	testConfig := func(configPath string, quiltPath []string) {
		if err := configRunOnce(configPath, quiltPath); err != nil {
			t.Errorf("%s failed validation: %s", configPath, err.Error())
		}
	}
	testConfig("example.spec", []string{"specs/stdlib"})
	testConfig("../quilt-tester/config/config.spec", []string{"specs/stdlib"})
	testConfig("./spark/sparkPI.spec",
		[]string{"./stdlib", "./spark", "./zookeeper"})
	testConfig("./wordpress/main.spec",
		[]string{"./stdlib", "./wordpress", "./spark", "./zookeeper"})
}
<|endoftext|>


<commit_before>package benchmark

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/version"
)

const userConfigKey = "user.lxd-benchmark"

// PrintServerInfo prints out information about the server.
func PrintServerInfo(c lxd.ContainerServer) error {
	server, _, err := c.GetServer()
	if err != nil {
		return err
	}
	env := server.Environment
	fmt.Printf("Test environment:\n")
	fmt.Printf("  Server backend: %s\n", env.Server)
	fmt.Printf("  Server version: %s\n", env.ServerVersion)
	fmt.Printf("  Kernel: %s\n", env.Kernel)
	fmt.Printf("  Kernel architecture: %s\n", env.KernelArchitecture)
	fmt.Printf("  Kernel version: %s\n", env.KernelVersion)
	fmt.Printf("  Storage backend: %s\n", env.Storage)
	fmt.Printf("  Storage version: %s\n", env.StorageVersion)
	fmt.Printf("  Container backend: %s\n", env.Driver)
	fmt.Printf("  Container version: %s\n", env.DriverVersion)
	fmt.Printf("\n")
	return nil
}

// LaunchContainers launches a set of containers.
func LaunchContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, start bool, freeze bool) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	printTestConfig(count, batchSize, image, privileged, freeze)

	fingerprint, err := ensureImage(c, image)
	if err != nil {
		return duration, err
	}

	startContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		name := getContainerName(count, index)

		err := createContainer(c, fingerprint, name, privileged)
		if err != nil {
			logf("Failed to launch container '%s': %s", name, err)
			return
		}

		if start {
			err := startContainer(c, name)
			if err != nil {
				logf("Failed to start container '%s': %s", name, err)
				return
			}

			if freeze {
				err := freezeContainer(c, name)
				if err != nil {
					logf("Failed to freeze container '%s': %s", name, err)
					return
				}
			}
		}
	}

	duration = processBatch(count, batchSize, startContainer)
	return duration, nil
}

// CreateContainers create the specified number of containers.
func CreateContainers(c lxd.ContainerServer, count int, parallel int, fingerprint string, privileged bool) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	createContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		name := getContainerName(count, index)

		err := createContainer(c, fingerprint, name, privileged)
		if err != nil {
			logf("Failed to launch container '%s': %s", name, err)
			return
		}
	}

	duration = processBatch(count, batchSize, createContainer)

	return duration, nil
}

// GetContainers returns containers created by the benchmark.
func GetContainers(c lxd.ContainerServer) ([]api.Container, error) {
	containers := []api.Container{}

	allContainers, err := c.GetContainers()
	if err != nil {
		return containers, err
	}

	for _, container := range allContainers {
		if container.Config[userConfigKey] == "true" {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

// StartContainers starts containers created by the benchmark.
func StartContainers(c lxd.ContainerServer, containers []api.Container, parallel int) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	count := len(containers)
	logf("Starting %d containers", count)

	startContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		container := containers[index]
		if !container.IsActive() {
			err := startContainer(c, container.Name)
			if err != nil {
				logf("Failed to start container '%s': %s", container.Name, err)
				return
			}
		}
	}

	duration = processBatch(count, batchSize, startContainer)
	return duration, nil
}

// StopContainers stops containers created by the benchmark.
func StopContainers(c lxd.ContainerServer, containers []api.Container, parallel int) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	count := len(containers)
	logf("Stopping %d containers", count)

	stopContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		container := containers[index]
		if container.IsActive() {
			err := stopContainer(c, container.Name)
			if err != nil {
				logf("Failed to stop container '%s': %s", container.Name, err)
				return
			}
		}
	}

	duration = processBatch(count, batchSize, stopContainer)
	return duration, nil
}

// DeleteContainers removes containers created by the benchmark.
func DeleteContainers(c lxd.ContainerServer, containers []api.Container, parallel int) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	count := len(containers)
	logf("Deleting %d containers", count)

	deleteContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		container := containers[index]
		name := container.Name
		if container.IsActive() {
			err := stopContainer(c, name)
			if err != nil {
				logf("Failed to stop container '%s': %s", name, err)
				return
			}
		}

		err = deleteContainer(c, name)
		if err != nil {
			logf("Failed to delete container: %s", name)
			return
		}
	}

	duration = processBatch(count, batchSize, deleteContainer)
	return duration, nil
}

func ensureImage(c lxd.ContainerServer, image string) (string, error) {
	var fingerprint string

	if strings.Contains(image, ":") {
		defaultConfig := config.DefaultConfig
		defaultConfig.UserAgent = version.UserAgent

		remote, fp, err := defaultConfig.ParseRemote(image)
		if err != nil {
			return "", err
		}
		fingerprint = fp

		imageServer, err := defaultConfig.GetImageServer(remote)
		if err != nil {
			return "", err
		}

		if fingerprint == "" {
			fingerprint = "default"
		}

		alias, _, err := imageServer.GetImageAlias(fingerprint)
		if err == nil {
			fingerprint = alias.Target
		}

		_, _, err = c.GetImage(fingerprint)
		if err != nil {
			logf("Importing image into local store: %s", fingerprint)
			image, _, err := imageServer.GetImage(fingerprint)
			if err != nil {
				logf("Failed to import image: %s", err)
				return "", err
			}

			err = copyImage(c, imageServer, *image)
			if err != nil {
				logf("Failed to import image: %s", err)
				return "", err
			}
		}
	} else {
		fingerprint = image
		alias, _, err := c.GetImageAlias(image)
		if err == nil {
			fingerprint = alias.Target
		} else {
			_, _, err = c.GetImage(image)
		}

		if err != nil {
			logf("Image not found in local store: %s", image)
			return "", err
		}
	}

	logf("Found image in local store: %s", fingerprint)
	return fingerprint, nil
}
<commit_msg>lxd-benchmark: use NewConfig to get a default configuration<commit_after>package benchmark

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/version"
)

const userConfigKey = "user.lxd-benchmark"

// PrintServerInfo prints out information about the server.
func PrintServerInfo(c lxd.ContainerServer) error {
	server, _, err := c.GetServer()
	if err != nil {
		return err
	}
	env := server.Environment
	fmt.Printf("Test environment:\n")
	fmt.Printf("  Server backend: %s\n", env.Server)
	fmt.Printf("  Server version: %s\n", env.ServerVersion)
	fmt.Printf("  Kernel: %s\n", env.Kernel)
	fmt.Printf("  Kernel architecture: %s\n", env.KernelArchitecture)
	fmt.Printf("  Kernel version: %s\n", env.KernelVersion)
	fmt.Printf("  Storage backend: %s\n", env.Storage)
	fmt.Printf("  Storage version: %s\n", env.StorageVersion)
	fmt.Printf("  Container backend: %s\n", env.Driver)
	fmt.Printf("  Container version: %s\n", env.DriverVersion)
	fmt.Printf("\n")
	return nil
}

// LaunchContainers launches a set of containers.
func LaunchContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, start bool, freeze bool) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	printTestConfig(count, batchSize, image, privileged, freeze)

	fingerprint, err := ensureImage(c, image)
	if err != nil {
		return duration, err
	}

	startContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		name := getContainerName(count, index)

		err := createContainer(c, fingerprint, name, privileged)
		if err != nil {
			logf("Failed to launch container '%s': %s", name, err)
			return
		}

		if start {
			err := startContainer(c, name)
			if err != nil {
				logf("Failed to start container '%s': %s", name, err)
				return
			}

			if freeze {
				err := freezeContainer(c, name)
				if err != nil {
					logf("Failed to freeze container '%s': %s", name, err)
					return
				}
			}
		}
	}

	duration = processBatch(count, batchSize, startContainer)
	return duration, nil
}

// CreateContainers create the specified number of containers.
func CreateContainers(c lxd.ContainerServer, count int, parallel int, fingerprint string, privileged bool) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	createContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		name := getContainerName(count, index)

		err := createContainer(c, fingerprint, name, privileged)
		if err != nil {
			logf("Failed to launch container '%s': %s", name, err)
			return
		}
	}

	duration = processBatch(count, batchSize, createContainer)

	return duration, nil
}

// GetContainers returns containers created by the benchmark.
func GetContainers(c lxd.ContainerServer) ([]api.Container, error) {
	containers := []api.Container{}

	allContainers, err := c.GetContainers()
	if err != nil {
		return containers, err
	}

	for _, container := range allContainers {
		if container.Config[userConfigKey] == "true" {
			containers = append(containers, container)
		}
	}

	return containers, nil
}

// StartContainers starts containers created by the benchmark.
func StartContainers(c lxd.ContainerServer, containers []api.Container, parallel int) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	count := len(containers)
	logf("Starting %d containers", count)

	startContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		container := containers[index]
		if !container.IsActive() {
			err := startContainer(c, container.Name)
			if err != nil {
				logf("Failed to start container '%s': %s", container.Name, err)
				return
			}
		}
	}

	duration = processBatch(count, batchSize, startContainer)
	return duration, nil
}

// StopContainers stops containers created by the benchmark.
func StopContainers(c lxd.ContainerServer, containers []api.Container, parallel int) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	count := len(containers)
	logf("Stopping %d containers", count)

	stopContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		container := containers[index]
		if container.IsActive() {
			err := stopContainer(c, container.Name)
			if err != nil {
				logf("Failed to stop container '%s': %s", container.Name, err)
				return
			}
		}
	}

	duration = processBatch(count, batchSize, stopContainer)
	return duration, nil
}

// DeleteContainers removes containers created by the benchmark.
func DeleteContainers(c lxd.ContainerServer, containers []api.Container, parallel int) (time.Duration, error) {
	var duration time.Duration

	batchSize, err := getBatchSize(parallel)
	if err != nil {
		return duration, err
	}

	count := len(containers)
	logf("Deleting %d containers", count)

	deleteContainer := func(index int, wg *sync.WaitGroup) {
		defer wg.Done()

		container := containers[index]
		name := container.Name
		if container.IsActive() {
			err := stopContainer(c, name)
			if err != nil {
				logf("Failed to stop container '%s': %s", name, err)
				return
			}
		}

		err = deleteContainer(c, name)
		if err != nil {
			logf("Failed to delete container: %s", name)
			return
		}
	}

	duration = processBatch(count, batchSize, deleteContainer)
	return duration, nil
}

func ensureImage(c lxd.ContainerServer, image string) (string, error) {
	var fingerprint string

	if strings.Contains(image, ":") {
		defaultConfig := config.NewConfig("", true)
		defaultConfig.UserAgent = version.UserAgent

		remote, fp, err := defaultConfig.ParseRemote(image)
		if err != nil {
			return "", err
		}
		fingerprint = fp

		imageServer, err := defaultConfig.GetImageServer(remote)
		if err != nil {
			return "", err
		}

		if fingerprint == "" {
			fingerprint = "default"
		}

		alias, _, err := imageServer.GetImageAlias(fingerprint)
		if err == nil {
			fingerprint = alias.Target
		}

		_, _, err = c.GetImage(fingerprint)
		if err != nil {
			logf("Importing image into local store: %s", fingerprint)
			image, _, err := imageServer.GetImage(fingerprint)
			if err != nil {
				logf("Failed to import image: %s", err)
				return "", err
			}

			err = copyImage(c, imageServer, *image)
			if err != nil {
				logf("Failed to import image: %s", err)
				return "", err
			}
		}
	} else {
		fingerprint = image
		alias, _, err := c.GetImageAlias(image)
		if err == nil {
			fingerprint = alias.Target
		} else {
			_, _, err = c.GetImage(image)
		}

		if err != nil {
			logf("Image not found in local store: %s", image)
			return "", err
		}
	}

	logf("Found image in local store: %s", fingerprint)
	return fingerprint, nil
}
<|endoftext|>


<commit_before>// +build amd64
// errorcheck -0 -d=ssa/check_bce/debug=3

// Copyright 2016 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.

// Test that the compiler does bounds check elimination as expected.
// This avoids accidental regressions.

package main

import "encoding/binary"

func f0(a []int) {
	a[0] = 1 // ERROR "Found IsInBounds$"
	a[0] = 1
	a[6] = 1 // ERROR "Found IsInBounds$"
	a[6] = 1
	a[5] = 1
	a[5] = 1
}

func f1(a [256]int, i int) {
	var j int
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j = i % 256
	useInt(a[j]) // ERROR "Found IsInBounds$"
	j = i & 255
	useInt(a[j])
	j = i & 17
	useInt(a[j])

	if 4 <= i && i < len(a) {
		useInt(a[i])
		useInt(a[i-1]) // ERROR "Found IsInBounds$"
		// TODO: 'if 4 <= i && i < len(a)' gets rewritten to 'if uint(i - 4) < 256 - 4',
		// which the bounds checker cannot yet use to infer that the next line doesn't need a bounds check.
		useInt(a[i-4])
	}
}

func f2(a [256]int, i uint) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i % 256
	useInt(a[j])
	j = i & 255
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f2a(a [35]int, i uint8) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i & 34
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f2b(a [35]int, i uint16) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i & 34
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f2c(a [35]int, i uint32) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i & 34
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f3(a [256]int, i uint8) {
	useInt(a[i])
	useInt(a[i+10])
	useInt(a[i+14])
}

func f4(a [27]int, i uint8) {
	useInt(a[i%15])
	useInt(a[i%19])
	useInt(a[i%27])
}

func f5(a []int) {
	if len(a) > 5 {
		useInt(a[5])
		useSlice(a[6:])
		useSlice(a[:6])
	}
}

func f6(a [32]int, b [64]int, i int) {
	useInt(a[uint32(i*0x07C4ACDD)>>27])
	useInt(b[uint64(i*0x07C4ACDD)>>58])
	useInt(a[uint(i*0x07C4ACDD)>>59])

	// The following bounds should not be removed because they can overflow.
	useInt(a[uint32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$"
	useInt(b[uint64(i*0x106297f105d0cc86)>>57]) // ERROR "Found IsInBounds$"
	useInt(a[int32(i*0x106297f105d0cc86)>>26])  // ERROR "Found IsInBounds$"
	useInt(b[int64(i*0x106297f105d0cc86)>>57])  // ERROR "Found IsInBounds$"
}

func g1(a []int) {
	for i := range a {
		a[i] = i
		useSlice(a[:i+1])
		useSlice(a[:i])
	}
}

func g2(a []int) {
	useInt(a[3]) // ERROR "Found IsInBounds$"
	useInt(a[2])
	useInt(a[1])
	useInt(a[0])
}

func g3(a []int) {
	for i := range a[:256] { // ERROR "Found IsSliceInBounds$"
		useInt(a[i]) // ERROR "Found IsInBounds$"
	}
	b := a[:256]
	for i := range b {
		useInt(b[i])
	}
}

func g4(a [100]int) {
	for i := 10; i < 50; i++ {
		useInt(a[i-10])
		useInt(a[i])
		useInt(a[i+25])
		useInt(a[i+50])

		// The following are out of bounds.
		useInt(a[i-11]) // ERROR "Found IsInBounds$"
		useInt(a[i+51]) // ERROR "Found IsInBounds$"
	}
}

func decode1(data []byte) (x uint64) {
	for len(data) >= 32 {
		x += binary.BigEndian.Uint64(data[:8])
		x += binary.BigEndian.Uint64(data[8:16])
		x += binary.BigEndian.Uint64(data[16:24])
		x += binary.BigEndian.Uint64(data[24:32])
		data = data[32:]
	}
	return x
}

func decode2(data []byte) (x uint64) {
	// TODO(rasky): this should behave like decode1 and compile to no
	// boundchecks. We're currently not able to remove all of them.
	for len(data) >= 32 {
		x += binary.BigEndian.Uint64(data)
		data = data[8:]
		x += binary.BigEndian.Uint64(data) // ERROR "Found IsInBounds$"
		data = data[8:]
		x += binary.BigEndian.Uint64(data) // ERROR "Found IsInBounds$"
		data = data[8:]
		x += binary.BigEndian.Uint64(data) // ERROR "Found IsInBounds$"
		data = data[8:]
	}
	return x
}

//go:noinline
func useInt(a int) {
}

//go:noinline
func useSlice(a []int) {
}

func main() {
}
<commit_msg>test: fix bcecheck test on noopt builder<commit_after>// +build amd64
// errorcheck -0 -d=ssa/check_bce/debug=3

// Copyright 2016 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.

// Test that the compiler does bounds check elimination as expected.
// This avoids accidental regressions.

package main

func f0(a []int) {
	a[0] = 1 // ERROR "Found IsInBounds$"
	a[0] = 1
	a[6] = 1 // ERROR "Found IsInBounds$"
	a[6] = 1
	a[5] = 1
	a[5] = 1
}

func f1(a [256]int, i int) {
	var j int
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j = i % 256
	useInt(a[j]) // ERROR "Found IsInBounds$"
	j = i & 255
	useInt(a[j])
	j = i & 17
	useInt(a[j])

	if 4 <= i && i < len(a) {
		useInt(a[i])
		useInt(a[i-1]) // ERROR "Found IsInBounds$"
		// TODO: 'if 4 <= i && i < len(a)' gets rewritten to 'if uint(i - 4) < 256 - 4',
		// which the bounds checker cannot yet use to infer that the next line doesn't need a bounds check.
		useInt(a[i-4])
	}
}

func f2(a [256]int, i uint) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i % 256
	useInt(a[j])
	j = i & 255
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f2a(a [35]int, i uint8) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i & 34
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f2b(a [35]int, i uint16) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i & 34
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f2c(a [35]int, i uint32) {
	useInt(a[i]) // ERROR "Found IsInBounds$"
	j := i & 34
	useInt(a[j])
	j = i & 17
	useInt(a[j])
}

func f3(a [256]int, i uint8) {
	useInt(a[i])
	useInt(a[i+10])
	useInt(a[i+14])
}

func f4(a [27]int, i uint8) {
	useInt(a[i%15])
	useInt(a[i%19])
	useInt(a[i%27])
}

func f5(a []int) {
	if len(a) > 5 {
		useInt(a[5])
		useSlice(a[6:])
		useSlice(a[:6])
	}
}

func f6(a [32]int, b [64]int, i int) {
	useInt(a[uint32(i*0x07C4ACDD)>>27])
	useInt(b[uint64(i*0x07C4ACDD)>>58])
	useInt(a[uint(i*0x07C4ACDD)>>59])

	// The following bounds should not be removed because they can overflow.
	useInt(a[uint32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$"
	useInt(b[uint64(i*0x106297f105d0cc86)>>57]) // ERROR "Found IsInBounds$"
	useInt(a[int32(i*0x106297f105d0cc86)>>26])  // ERROR "Found IsInBounds$"
	useInt(b[int64(i*0x106297f105d0cc86)>>57])  // ERROR "Found IsInBounds$"
}

func g1(a []int) {
	for i := range a {
		a[i] = i
		useSlice(a[:i+1])
		useSlice(a[:i])
	}
}

func g2(a []int) {
	useInt(a[3]) // ERROR "Found IsInBounds$"
	useInt(a[2])
	useInt(a[1])
	useInt(a[0])
}

func g3(a []int) {
	for i := range a[:256] { // ERROR "Found IsSliceInBounds$"
		useInt(a[i]) // ERROR "Found IsInBounds$"
	}
	b := a[:256]
	for i := range b {
		useInt(b[i])
	}
}

func g4(a [100]int) {
	for i := 10; i < 50; i++ {
		useInt(a[i-10])
		useInt(a[i])
		useInt(a[i+25])
		useInt(a[i+50])

		// The following are out of bounds.
		useInt(a[i-11]) // ERROR "Found IsInBounds$"
		useInt(a[i+51]) // ERROR "Found IsInBounds$"
	}
}

func Uint64(b []byte) uint64 {
	_ = b[7] // ERROR "Found IsInBounds$"
	return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
		uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}

func decode1(data []byte) (x uint64) {
	for len(data) >= 32 {
		x += Uint64(data[:8])
		x += Uint64(data[8:16])
		x += Uint64(data[16:24])
		x += Uint64(data[24:32])
		data = data[32:]
	}
	return x
}

func decode2(data []byte) (x uint64) {
	// TODO(rasky): this should behave like decode1 and compile to no
	// boundchecks. We're currently not able to remove all of them.
	for len(data) >= 32 {
		x += Uint64(data)
		data = data[8:]
		x += Uint64(data) // ERROR "Found IsInBounds$"
		data = data[8:]
		x += Uint64(data) // ERROR "Found IsInBounds$"
		data = data[8:]
		x += Uint64(data) // ERROR "Found IsInBounds$"
		data = data[8:]
	}
	return x
}

//go:noinline
func useInt(a int) {
}

//go:noinline
func useSlice(a []int) {
}

func main() {
}
<|endoftext|>


<commit_before>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

import (
	"context"
	"database/sql"
)

type baseStmt struct {
	engine Engine
}

type queryStmt struct {
	SQLer
	baseStmt
}

type execStmt struct {
	SQLer
	baseStmt
}

type ddlStmt struct {
	DDLSQLer
	baseStmt
}

func newQueryStmt(e Engine, sql SQLer) *queryStmt {
	return &queryStmt{
		SQLer: sql,
		baseStmt: baseStmt{
			engine: e,
		},
	}
}

func newExecStmt(e Engine, sql SQLer) *execStmt {
	return &execStmt{
		SQLer: sql,
		baseStmt: baseStmt{
			engine: e,
		},
	}
}

func newDDLStmt(e Engine, sql DDLSQLer) *ddlStmt {
	return &ddlStmt{
		DDLSQLer: sql,
		baseStmt: baseStmt{
			engine: e,
		},
	}
}

func (stmt *baseStmt) Dialect() Dialect {
	return stmt.engine.Dialect()
}

func (stmt *baseStmt) Engine() Engine {
	return stmt.engine
}

func (stmt ddlStmt) Exec() error {
	return stmt.ExecContext(context.Background())
}

func (stmt *ddlStmt) ExecContext(ctx context.Context) error {
	qs, err := stmt.DDLSQL()
	if err != nil {
		return err
	}

	for k, v := range qs {
		if qs[k], _, err = stmt.Dialect().SQL(v, nil); err != nil {
			return err
		}
	}

	for _, query := range qs {
		if _, err = stmt.Engine().ExecContext(ctx, query); err != nil {
			return err
		}
	}

	return nil
}

func (stmt *execStmt) Exec() (sql.Result, error) {
	return stmt.ExecContext(context.Background())
}

func (stmt *execStmt) ExecContext(ctx context.Context) (sql.Result, error) {
	query, args, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().ExecContext(ctx, query, args...)
}

func (stmt *execStmt) Prepare() (*sql.Stmt, error) {
	return stmt.PrepareContext(context.Background())
}

func (stmt *execStmt) PrepareContext(ctx context.Context) (*sql.Stmt, error) {
	query, _, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().PrepareContext(ctx, query)
}

func (stmt *queryStmt) Prepare() (*sql.Stmt, error) {
	return stmt.PrepareContext(context.Background())
}

func (stmt *queryStmt) PrepareContext(ctx context.Context) (*sql.Stmt, error) {
	query, _, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().PrepareContext(ctx, query)
}

func (stmt queryStmt) Query() (*sql.Rows, error) {
	return stmt.QueryContext(context.Background())
}

func (stmt *queryStmt) QueryContext(ctx context.Context) (*sql.Rows, error) {
	query, args, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().QueryContext(ctx, query, args...)
}
<commit_msg>[sqlbuilder] 修正 ddl.ExecContext 多次执行 Dialect.SQL 的错误<commit_after>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

import (
	"context"
	"database/sql"
)

type baseStmt struct {
	engine Engine
}

type queryStmt struct {
	SQLer
	baseStmt
}

type execStmt struct {
	SQLer
	baseStmt
}

type ddlStmt struct {
	DDLSQLer
	baseStmt
}

func newQueryStmt(e Engine, sql SQLer) *queryStmt {
	return &queryStmt{
		SQLer: sql,
		baseStmt: baseStmt{
			engine: e,
		},
	}
}

func newExecStmt(e Engine, sql SQLer) *execStmt {
	return &execStmt{
		SQLer: sql,
		baseStmt: baseStmt{
			engine: e,
		},
	}
}

func newDDLStmt(e Engine, sql DDLSQLer) *ddlStmt {
	return &ddlStmt{
		DDLSQLer: sql,
		baseStmt: baseStmt{
			engine: e,
		},
	}
}

func (stmt *baseStmt) Dialect() Dialect {
	return stmt.engine.Dialect()
}

func (stmt *baseStmt) Engine() Engine {
	return stmt.engine
}

func (stmt ddlStmt) Exec() error {
	return stmt.ExecContext(context.Background())
}

func (stmt *ddlStmt) ExecContext(ctx context.Context) error {
	qs, err := stmt.DDLSQL()
	if err != nil {
		return err
	}

	for _, query := range qs {
		if _, err = stmt.Engine().ExecContext(ctx, query); err != nil {
			return err
		}
	}

	return nil
}

func (stmt *execStmt) Exec() (sql.Result, error) {
	return stmt.ExecContext(context.Background())
}

func (stmt *execStmt) ExecContext(ctx context.Context) (sql.Result, error) {
	query, args, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().ExecContext(ctx, query, args...)
}

func (stmt *execStmt) Prepare() (*sql.Stmt, error) {
	return stmt.PrepareContext(context.Background())
}

func (stmt *execStmt) PrepareContext(ctx context.Context) (*sql.Stmt, error) {
	query, _, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().PrepareContext(ctx, query)
}

func (stmt *queryStmt) Prepare() (*sql.Stmt, error) {
	return stmt.PrepareContext(context.Background())
}

func (stmt *queryStmt) PrepareContext(ctx context.Context) (*sql.Stmt, error) {
	query, _, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().PrepareContext(ctx, query)
}

func (stmt queryStmt) Query() (*sql.Rows, error) {
	return stmt.QueryContext(context.Background())
}

func (stmt *queryStmt) QueryContext(ctx context.Context) (*sql.Rows, error) {
	query, args, err := stmt.SQL()
	if err != nil {
		return nil, err
	}

	return stmt.Engine().QueryContext(ctx, query, args...)
}
<|endoftext|>


<commit_before>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

// CreateViewStmt 创建视图的语句
type CreateViewStmt struct {
	*ddlStmt

	selectStmt  *SelectStmt
	ViewName    string
	Columns     []string
	IsTemporary bool
	IsReplace   bool
}

// CreateViewStmtHooker CreateViewStmt.DDLSQL 的钩子函数
type CreateViewStmtHooker interface {
	CreateViewStmtHook(*CreateViewStmt) ([]string, error)
}

// CreateView 创建视图
func CreateView(e Engine, d Dialect) *CreateViewStmt {
	stmt := &CreateViewStmt{}
	stmt.ddlStmt = newDDLStmt(e, d, stmt)

	return stmt
}

// View 将当前查询语句转换为视图
func (stmt *SelectStmt) View(name string) *CreateViewStmt {
	return CreateView(stmt.Engine(), stmt.Dialect()).
		From(stmt)
}

// Reset 重置对象
func (stmt *CreateViewStmt) Reset() *CreateViewStmt {
	stmt.ViewName = ""
	stmt.selectStmt = nil
	stmt.Columns = stmt.Columns[:0]
	stmt.IsTemporary = false
	stmt.IsReplace = false

	return stmt
}

// Column 指定视图的列，如果未指定，则会直接采用 Select 中的列信息
func (stmt *CreateViewStmt) Column(col ...string) *CreateViewStmt {
	if stmt.Columns == nil {
		stmt.Columns = col
		return stmt
	}

	stmt.Columns = append(stmt.Columns, col...)
	return stmt
}

// Name 指定视图名称
func (stmt *CreateViewStmt) Name(name string) *CreateViewStmt {
	stmt.ViewName = name
	return stmt
}

// Temporary 临时视图
func (stmt *CreateViewStmt) Temporary() *CreateViewStmt {
	stmt.IsTemporary = true
	return stmt
}

// Replace 如果已经存在，则更新视图内容
func (stmt *CreateViewStmt) Replace() *CreateViewStmt {
	stmt.IsReplace = true
	return stmt
}

// SelectQuery 获取 SELECT 的查询内容内容
func (stmt *CreateViewStmt) SelectQuery() (string, error) {
	if stmt.selectStmt == nil {
		return "", ErrValueIsEmpty
	}

	query, args, err := stmt.selectStmt.SQL()
	if err != nil {
		return "", err
	}

	if len(args) > 0 {
		return "", ErrViewSelectNotAllowArgs
	}

	return query, nil
}

// From 指定 Select 语句
//
// 在调用 DDLSQL 之前，可以继续修改 sel 的内容。
func (stmt *CreateViewStmt) From(sel *SelectStmt) *CreateViewStmt {
	stmt.selectStmt = sel
	return stmt
}

// DDLSQL 返回创建视图的 SQL 语句
func (stmt *CreateViewStmt) DDLSQL() ([]string, error) {
	if stmt.ViewName == "" {
		return nil, ErrTableIsEmpty
	}

	if hook, ok := stmt.Dialect().(CreateViewStmtHooker); ok {
		return hook.CreateViewStmtHook(stmt)
	}

	selectQuery, err := stmt.SelectQuery()
	if err != nil {
		return nil, err
	}

	builder := New("CREATE ")

	if stmt.IsReplace {
		builder.WriteString(" OR REPLACE ")
	}

	if stmt.IsTemporary {
		builder.WriteString(" TEMPORARY ")
	}

	builder.WriteString(" VIEW ").
		WriteBytes(stmt.l).
		WriteString(stmt.ViewName).
		WriteBytes(stmt.r)

	if len(stmt.Columns) > 0 {
		builder.WriteBytes('(')
		for _, col := range stmt.Columns {
			builder.WriteBytes(stmt.l).
				WriteString(col).
				WriteBytes(stmt.r, ',')
		}
		builder.TruncateLast(1).WriteBytes(')')
	}

	builder.WriteString(selectQuery)

	return []string{builder.String()}, nil
}
<commit_msg>[sqlbuilder] 精简代码<commit_after>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

// CreateViewStmt 创建视图的语句
type CreateViewStmt struct {
	*ddlStmt

	selectStmt  *SelectStmt
	ViewName    string
	Columns     []string
	IsTemporary bool
	IsReplace   bool
}

// CreateViewStmtHooker CreateViewStmt.DDLSQL 的钩子函数
type CreateViewStmtHooker interface {
	CreateViewStmtHook(*CreateViewStmt) ([]string, error)
}

// CreateView 创建视图
func CreateView(e Engine, d Dialect) *CreateViewStmt {
	stmt := &CreateViewStmt{}
	stmt.ddlStmt = newDDLStmt(e, d, stmt)

	return stmt
}

// View 将当前查询语句转换为视图
func (stmt *SelectStmt) View(name string) *CreateViewStmt {
	return CreateView(stmt.Engine(), stmt.Dialect()).
		From(stmt)
}

// Reset 重置对象
func (stmt *CreateViewStmt) Reset() *CreateViewStmt {
	stmt.ViewName = ""
	stmt.selectStmt = nil
	stmt.Columns = stmt.Columns[:0]
	stmt.IsTemporary = false
	stmt.IsReplace = false

	return stmt
}

// Column 指定视图的列，如果未指定，则会直接采用 Select 中的列信息
func (stmt *CreateViewStmt) Column(col ...string) *CreateViewStmt {
	if stmt.Columns == nil {
		stmt.Columns = col
		return stmt
	}

	stmt.Columns = append(stmt.Columns, col...)
	return stmt
}

// Name 指定视图名称
func (stmt *CreateViewStmt) Name(name string) *CreateViewStmt {
	stmt.ViewName = name
	return stmt
}

// Temporary 临时视图
func (stmt *CreateViewStmt) Temporary() *CreateViewStmt {
	stmt.IsTemporary = true
	return stmt
}

// Replace 如果已经存在，则更新视图内容
func (stmt *CreateViewStmt) Replace() *CreateViewStmt {
	stmt.IsReplace = true
	return stmt
}

// SelectQuery 获取 SELECT 的查询内容内容
func (stmt *CreateViewStmt) SelectQuery() (string, error) {
	if stmt.selectStmt == nil {
		return "", ErrValueIsEmpty
	}

	query, args, err := stmt.selectStmt.SQL()
	if err != nil {
		return "", err
	}

	if len(args) > 0 {
		return "", ErrViewSelectNotAllowArgs
	}

	return query, nil
}

// From 指定 Select 语句
//
// 在调用 DDLSQL 之前，可以继续修改 sel 的内容。
func (stmt *CreateViewStmt) From(sel *SelectStmt) *CreateViewStmt {
	stmt.selectStmt = sel
	return stmt
}

// DDLSQL 返回创建视图的 SQL 语句
func (stmt *CreateViewStmt) DDLSQL() ([]string, error) {
	if stmt.ViewName == "" {
		return nil, ErrTableIsEmpty
	}

	if hook, ok := stmt.Dialect().(CreateViewStmtHooker); ok {
		return hook.CreateViewStmtHook(stmt)
	}

	selectQuery, err := stmt.SelectQuery()
	if err != nil {
		return nil, err
	}

	builder := New("CREATE ")

	if stmt.IsReplace {
		builder.WriteString(" OR REPLACE ")
	}

	if stmt.IsTemporary {
		builder.WriteString(" TEMPORARY ")
	}

	builder.WriteString(" VIEW ").Quote(stmt.ViewName, stmt.l, stmt.r)

	if len(stmt.Columns) > 0 {
		builder.WriteBytes('(')
		for _, col := range stmt.Columns {
			builder.Quote(col, stmt.l, stmt.r).
				WriteBytes(',')
		}
		builder.TruncateLast(1).WriteBytes(')')
	}

	builder.WriteString(selectQuery)

	return []string{builder.String()}, nil
}
<|endoftext|>


<commit_before>
package sqlutil

import (
    "fmt"
    "strings"
    "regexp"
    "github.com/princeofdatamining/golib/sqlutil/dialect"
)

var (
    sFrom    = "FROM"
    sAs      = "AS"
    sGroupBy = "GROUP BY"
    sHaving  = "HAVING"
    sOrderBy = "ORDER BY"
    sLimit   = "LIMIT"
)
const (
    nMaxNavPage = 10
    nRowsPerPage = 20
)
func NewSQLQuery(t TableMap, args ...string) (m *SQLQuery) {
    m = &SQLQuery{
        page_maxNav: nMaxNavPage,
        page_perRows: nRowsPerPage,
    }
    var as string
    if args != nil {
        as = args[0]
    }
    m.bind(t, as)
    return
}
type SQLQuery struct {
    dialect dialect.Dialect
    db      *dbMap
    table   *tableMap
    fields  string
    as      string
    joins   string
    wheres  string
    groups  string
    having  string
    orders  string
    limits  string
    //
    page_grouping   bool
    page_maxNav     int
    page_perRows    int
    page_sql        string
    page_sql_count  string
    page_allRows    int
    page_count      int
    page_base       int
    page_last       int
    page_from       int
    page_upto       int
    page_curr       int
}
func (this *SQLQuery) bind(t TableMap, as string) () {
    this.table, _ = t.(*tableMap)
    this.db = this.table.dbmap
    this.dialect = this.db.dialect
    this.as = strings.TrimSpace(as)
}
func isAll(s string) (bool) {
    switch s {
    case "", "*", "all", "ALL":
        return true
    }
    return false
}
func (this *SQLQuery) SetFields(s string) () {
    if s = strings.TrimSpace(s); s == "" {
        if this.as == "" {
            s = this.as
        } else {
            s = this.table.quoteTable()
        }
        s += ".*"
    }
    this.fields = s
}
func (this *SQLQuery) SetJoin(s string) () {
    if s = strings.TrimSpace(s); false {
        //
    }
    this.joins = s
}
func (this *SQLQuery) SetWhere(s string) () {
    if s = strings.TrimSpace(s); isAll(s) {
        s = "1"
    }
    this.wheres = s
}
func (this *SQLQuery) SetGroup(s string) () {
    if s = strings.TrimSpace(s); s != "" {
        s = sGroupBy + " " + s
    }
    this.groups = s
}
func (this *SQLQuery) SetHaving(s string) () {
    if s = strings.TrimSpace(s); s != "" {
        s = sHaving + " " + s
    }
    this.having = s
}
func (this *SQLQuery) SetOrderBy(s string) () {
    if s = strings.TrimSpace(s); s != "" {
        s = sOrderBy + " " + s
    }
    this.orders = s
}
func (this *SQLQuery) SetLimit(s string) () {
    if s = strings.TrimSpace(s); isAll(s) {
        s = ""
    } else {
        s = sLimit + " " + s
    }
    this.limits = s
}
var (
    reTableAs = regexp.MustCompile(sFrom + "\\s+\\S+")
    reLimit = regexp.MustCompile("^\\s*" + sLimit)
    reGroup = regexp.MustCompile("^\\s*" + sGroupBy)
)
func (this *SQLQuery) MakeSQL(pageMode bool, suffixes ...string) (s string) {
    selSQL := this.dialect.SelectSQL(this.table.schemaName, this.table.tableName)
    if this.as != "" {
        selSQL = reTableAs.ReplaceAllString(selSQL, "${0} " + sAs + " " + this.as)
    }
    var suffix string
    if this.groups != "" {
        suffix += " " + this.groups
        this.page_grouping = true
    }
    if this.having != "" {
        suffix += " " + this.having
    }
    if this.orders != "" {
        suffix += " " + this.orders
    }
    if this.limits != "" && !pageMode {
        suffix += " " + this.limits
    }
    for _, sfx := range suffixes {
        if sfx = strings.TrimSpace(sfx); sfx != "" {
            if !pageMode || !reLimit.MatchString(sfx) {
                suffix += " " + sfx
            }
            this.page_grouping = reGroup.MatchString(sfx)
        }
    }
    this.page_sql_count = fmt.Sprintf(selSQL, "", "COUNT(*)", this.joins, this.wheres, "")
    return fmt.Sprintf(selSQL, "", this.fields, this.joins, this.wheres, suffix)
}
func (this *SQLQuery) Get (slices interface{}, exec SQLExecutor,                       suffixes ...string) (int64, error) {
    if exec == nil {
        exec = this.table
    }
    return exec.SelectAll(slices, this.MakeSQL(false, suffixes...))
}
func (this *SQLQuery) Get1(slices interface{}, exec SQLExecutor,         where string, suffixes ...string) (int64, error) {
    this.SetWhere(where)
    return this.Get(slices, exec, suffixes...)
}
func (this *SQLQuery) Get2(slices interface{}, exec SQLExecutor, fields, where string, suffixes ...string) (int64, error) {
    this.SetFields(fields)
    this.SetWhere(where)
    return this.Get(slices, exec, suffixes...)
}

func (this *SQLQuery) SetPageMode(maxNavPage, rowsPerPage int) () {
    if this.page_maxNav = maxNavPage; maxNavPage < 1 {
        this.page_maxNav = nMaxNavPage
    }
    if this.page_perRows = rowsPerPage; rowsPerPage < 1 {
        this.page_perRows = nRowsPerPage
    }
}
func (this *SQLQuery) InitPage(suffixes ...string) () {
    this.page_sql = this.MakeSQL(true, suffixes...)
    //
    if this.page_grouping {
        this.page_sql_count = fmt.Sprintf("SELECT COUNT(*) FROM (%s)", this.page_sql)
    }
    rows, _ := this.table.SelectInt(this.page_sql_count)
    allRows := int(rows)
    //
    this.page_allRows = allRows
    this.page_count = (allRows + this.page_perRows - 1) / this.page_perRows
    this.page_base = 1
    this.page_last = this.page_base + this.page_count - 1
    //
    this.page_from = this.page_base
    if this.page_upto = this.page_from + this.page_maxNav - 1; this.page_upto > this.page_last {
        this.page_upto = this.page_last
    }
}
func (this *SQLQuery) InitPage1(        where string, suffixes ...string) () {
    this.SetWhere(where)
    this.InitPage(suffixes...)
}
func (this *SQLQuery) InitPage2(fields, where string, suffixes ...string) () {
    this.SetFields(fields)
    this.SetWhere(where)
    this.InitPage(suffixes...)
}
func (this *SQLQuery) GetPage(slices interface{}, pageNo int) (rows int64, err error) {
    if (this.page_allRows <= 0) {
        return
    }
    switch {
    case pageNo < this.page_base:
        this.page_curr = this.page_base
    case pageNo > this.page_last:
        this.page_curr = this.page_last
    default:
        this.page_curr = pageNo
    }
    if this.page_from = this.page_curr - (this.page_maxNav/2); this.page_from < this.page_base {
        this.page_from = this.page_base
    }
    if this.page_upto = this.page_from + (this.page_maxNav-1); this.page_upto > this.page_last {
        this.page_upto = this.page_last
        if this.page_from = this.page_upto+1-this.page_maxNav; this.page_from < this.page_base {
            this.page_from = this.page_base
        }
    }
    start := this.page_perRows * (this.page_curr-this.page_base)
    s := fmt.Sprintf("%s %s %d,%d", this.page_sql, sLimit, start, this.page_perRows)
    return this.table.SelectAll(slices, s)
}
func (this *SQLQuery) GetPageBar(pattern string) (h string) {
    return
}
<commit_msg>* sqlutil/complex.go: SQLQuery: + paging utils like php/iweb/IPaging<commit_after>
package sqlutil

import (
    "fmt"
    "strings"
    "regexp"
    "github.com/princeofdatamining/golib/sqlutil/dialect"
)

var (
    sFrom    = "FROM"
    sAs      = "AS"
    sGroupBy = "GROUP BY"
    sHaving  = "HAVING"
    sOrderBy = "ORDER BY"
    sLimit   = "LIMIT"
)
const (
    nMaxNavPage = 10
    nRowsPerPage = 20
)
func NewSQLQuery(t TableMap, args ...string) (m *SQLQuery) {
    m = &SQLQuery{
        page_maxNav: nMaxNavPage,
        page_perRows: nRowsPerPage,
    }
    var as string
    if args != nil {
        as = args[0]
    }
    m.bind(t, as)
    return
}
type SQLQuery struct {
    dialect dialect.Dialect
    db      *dbMap
    table   *tableMap
    fields  string
    as      string
    joins   string
    wheres  string
    groups  string
    having  string
    orders  string
    limits  string
    //
    page_grouping   bool
    page_maxNav     int
    page_perRows    int
    page_sql        string
    page_sql_count  string
    page_allRows    int
    page_count      int
    page_first      int
    page_last       int
    page_left       int
    page_right      int
    page_select     int
}
func (this *SQLQuery) bind(t TableMap, as string) () {
    this.table, _ = t.(*tableMap)
    this.db = this.table.dbmap
    this.dialect = this.db.dialect
    this.as = strings.TrimSpace(as)
}
func isAll(s string) (bool) {
    switch s {
    case "", "*", "all", "ALL":
        return true
    }
    return false
}
func (this *SQLQuery) SetFields(s string) () {
    if s = strings.TrimSpace(s); s == "" {
        if this.as != "" {
            s = this.as
        } else {
            s = this.table.quoteTable()
        }
        s += ".*"
    }
    this.fields = s
}
func (this *SQLQuery) SetJoin(s string) () {
    if s = strings.TrimSpace(s); false {
        //
    }
    this.joins = s
}
func (this *SQLQuery) SetWhere(s string) () {
    if s = strings.TrimSpace(s); isAll(s) {
        s = "1"
    }
    this.wheres = s
}
func (this *SQLQuery) SetGroupBy(s string) () {
    if s = strings.TrimSpace(s); s != "" {
        s = sGroupBy + " " + s
    }
    this.groups = s
}
func (this *SQLQuery) SetHaving(s string) () {
    if s = strings.TrimSpace(s); s != "" {
        s = sHaving + " " + s
    }
    this.having = s
}
func (this *SQLQuery) SetOrderBy(s string) () {
    if s = strings.TrimSpace(s); s != "" {
        s = sOrderBy + " " + s
    }
    this.orders = s
}
func (this *SQLQuery) SetLimit(s string) () {
    if s = strings.TrimSpace(s); isAll(s) {
        s = ""
    } else {
        s = sLimit + " " + s
    }
    this.limits = s
}
var (
    reTableAs = regexp.MustCompile(sFrom + "\\s+\\S+")
    reLimit = regexp.MustCompile("^\\s*" + sLimit)
    reGroup = regexp.MustCompile("^\\s*" + sGroupBy)
    reSemicolon = regexp.MustCompile(";\\s*$")
)
func (this *SQLQuery) MakeSQL(pageMode bool, suffixes ...string) (s string) {
    selSQL := this.dialect.SelectSQL(this.table.schemaName, this.table.tableName)
    if this.as != "" {
        selSQL = reTableAs.ReplaceAllString(selSQL, "${0} " + sAs + " " + this.as)
    }
    var suffix string
    if this.groups != "" {
        suffix += " " + this.groups
        this.page_grouping = true
    }
    if this.having != "" {
        suffix += " " + this.having
    }
    if this.orders != "" {
        suffix += " " + this.orders
    }
    if this.limits != "" && !pageMode {
        suffix += " " + this.limits
    }
    for _, sfx := range suffixes {
        if sfx = strings.TrimSpace(sfx); sfx != "" {
            if !pageMode || !reLimit.MatchString(sfx) {
                suffix += " " + sfx
            }
            this.page_grouping = reGroup.MatchString(sfx)
        }
    }
    this.page_sql_count = fmt.Sprintf(selSQL, "", "COUNT(*)", this.joins, this.wheres, "")
    var page_suffix string
    if pageMode {
        page_suffix = " %s"
    }
    return fmt.Sprintf(selSQL, "", this.fields, this.joins, this.wheres, suffix + page_suffix)
}
func (this *SQLQuery) Get (slices interface{}, exec SQLExecutor,                       suffixes ...string) (int64, error) {
    if exec == nil {
        exec = this.table
    }
    return exec.SelectAll(slices, this.MakeSQL(false, suffixes...))
}
func (this *SQLQuery) Get1(slices interface{}, exec SQLExecutor,         where string, suffixes ...string) (int64, error) {
    this.SetWhere(where)
    return this.Get(slices, exec, suffixes...)
}
func (this *SQLQuery) Get2(slices interface{}, exec SQLExecutor, fields, where string, suffixes ...string) (int64, error) {
    this.SetFields(fields)
    this.SetWhere(where)
    return this.Get(slices, exec, suffixes...)
}

func (this *SQLQuery) SetPageMode(maxNavPage, rowsPerPage int) () {
    if this.page_maxNav = maxNavPage; maxNavPage < 1 {
        this.page_maxNav = nMaxNavPage
    }
    if this.page_perRows = rowsPerPage; rowsPerPage < 1 {
        this.page_perRows = nRowsPerPage
    }
}
func (this *SQLQuery) InitPage(suffixes ...string) () {
    this.page_sql = this.MakeSQL(true, suffixes...)
    //
    if this.page_grouping {
        s := reSemicolon.ReplaceAllString(this.page_sql, "")
        this.page_sql_count = fmt.Sprintf("SELECT COUNT(*) FROM (%s) AS IPaging;", fmt.Sprintf(s, ""))
    }
    rows, _ := this.table.SelectInt(this.page_sql_count)
    allRows := int(rows)
    //
    this.page_allRows = allRows
    this.page_count = (allRows + this.page_perRows - 1) / this.page_perRows
    this.page_first = 1
    this.page_last = this.page_first + this.page_count - 1
    //
    this.page_left = this.page_first
    if this.page_right = this.page_left + this.page_maxNav - 1; this.page_right > this.page_last {
        this.page_right = this.page_last
    }
}
func (this *SQLQuery) InitPage1(        where string, suffixes ...string) () {
    this.SetWhere(where)
    this.InitPage(suffixes...)
}
func (this *SQLQuery) InitPage2(fields, where string, suffixes ...string) () {
    this.SetFields(fields)
    this.SetWhere(where)
    this.InitPage(suffixes...)
}
func (this *SQLQuery) GetPage(slices interface{}, pageNo int) (rows int64, err error) {
    if (this.page_allRows <= 0) {
        return
    }
    switch {
    case pageNo < this.page_first:
        this.page_select = this.page_first
    case pageNo > this.page_last:
        this.page_select = this.page_last
    default:
        this.page_select = pageNo
    }
    if this.page_left      = this.page_select - (this.page_maxNav/2); this.page_left  < this.page_first {
        this.page_left     = this.page_first
    }
    if this.page_right     = this.page_left  + (this.page_maxNav-1); this.page_right > this.page_last   {
        this.page_right    = this.page_last
        if this.page_left  = this.page_right - (this.page_maxNav-1); this.page_left  < this.page_first  {
            this.page_left = this.page_first
        }
    }
    start := this.page_perRows * (this.page_select-this.page_first)
    s := fmt.Sprintf(this.page_sql, fmt.Sprintf("%s %d,%d", sLimit, start, this.page_perRows))
    return this.table.SelectAll(slices, s)
}
var HTML_Paging = `
<![CDATA[FIRST]]>
<![CDATA[PREVN]]>
<![CDATA[PREV]]>
<![CDATA[BODY]]>
<![CDATA[NEXT]]>
<![CDATA[NEXTN]]>
<![CDATA[LAST]]>
<![CDATA[INFO]]>
`
var HTML_Paging_Elements = map[string]string{
    "FIRST": "[page] : href='[href]'",
    "PREVN": "[page] : href='[href]'",
    "PREV" : "[page] : href='[href]'",
    "NEXT" : "[page] : href='[href]'",
    "NEXTN": "[page] : href='[href]'",
    "LAST" : "[page] : href='[href]'",
    "INFO" : "[page]/[count]",
    //
    "SELECT": "\t[page]# href='[href]'\n",
    "AROUND": "\thref='[href]', [page]\n",
}
func (this *SQLQuery) GetPageBar(pattern string, elements map[string]string, f func (page int) (string)) (h string) {
    var (
        elem, body string
        page int
    )
    h = pattern
    p := func (page int) (string) { return fmt.Sprintf("%d", page) }
    //
    elem, page = "FIRST", this.page_first
    if replace, ok := elements[elem]; ok && this.page_select != page {
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[href]", f(page), -1)
        h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), replace, 1)
    }
    elem, page = "LAST" , this.page_last
    if replace, ok := elements[elem]; ok && this.page_select != page {
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[href]", f(page), -1)
        h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), replace, 1)
    }
    //
    elem, page = "PREVN", this.page_select - this.page_maxNav
    if replace, ok := elements[elem]; ok && page >= this.page_first {
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[href]", f(page), -1)
        h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), replace, 1)
    }
    elem, page = "NEXTN", this.page_select + this.page_maxNav
    if replace, ok := elements[elem]; ok && page <= this.page_last  {
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[href]", f(page), -1)
        h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), replace, 1)
    }
    //
    elem, page = "PREV" , this.page_select - 1
    if replace, ok := elements[elem]; ok && page >= this.page_first {
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[href]", f(page), -1)
        h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), replace, 1)
    }
    elem, page = "NEXT" , this.page_select + 1
    if replace, ok := elements[elem]; ok && page <= this.page_last  {
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[href]", f(page), -1)
        h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), replace, 1)
    }
    //
    elem, page = "INFO" , this.page_select
    if replace, ok := elements[elem]; ok {
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[count]",p(this.page_count), -1)
        h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), replace, 1)
    }
    elem, body = "BODY" , ""
    repSelect, repAround := elements["SELECT"], elements["AROUND"]
    for page = this.page_left; page <= this.page_right; page++ {
        replace := repAround
        if page == this.page_select { replace = repSelect }
        replace = strings.Replace(replace, "[page]", p(page), -1)
        replace = strings.Replace(replace, "[href]", f(page), -1)
        body += replace
    }
    h = strings.Replace(h, fmt.Sprintf("<![CDATA[%s]]>", elem), body, 1)
    //
    return
}
<|endoftext|>


<commit_before>package sqm

import (
	"io/ioutil"
	"testing"
)

func TestStructure(t *testing.T) {
	class := &Class{Name: "mission"}
	if class.Props != nil {
		t.Errorf("Class has props")
	}
	if len(class.Props) > 0 {
		t.Errorf("Props length is %d", len(class.Props))
	}
	class.Props = append(class.Props, &Property{"test", TInt, "value"})
	if class.Props == nil {
		t.Errorf("Class has no props")
	}

	if len(class.Props) != 1 {
		t.Errorf("Props length is %d", len(class.Props))
	}
}

type parseTest struct {
	name  string
	input string
	class tclass
}

type tclass struct {
	name     string
	props    []Property
	arrprops []ArrayProperty
	classes  []tclass
}

var parseTests = []parseTest{
	{
		"simple class", "class testclass { version=11; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"testclass",
					[]Property{
						{"version", TInt, "11"},
					},
					[]ArrayProperty{},
					[]tclass{},
				},
			},
		},
	},

	{
		"attributes", "version=11; string=\"teststring\"; float1=123.456; float2=+123.456;",
		tclass{"mission",
			[]Property{
				{"version", TInt, "11"},
				{"string", TString, "teststring"},
				{"float1", TFloat, "123.456"},
				{"float2", TFloat, "+123.456"},
			},
			[]ArrayProperty{},
			[]tclass{},
		},
	},

	{
		"array int attributes", "arr[]={1,2,3};",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{
				{"arr", TInt, []string{"1", "2", "3"}},
			},
			[]tclass{},
		},
	},

	{
		"subclass array int attributes", "class test { arr[]={1,2,3}; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"test",
					[]Property{},
					[]ArrayProperty{
						{"arr", TInt, []string{"1", "2", "3"}},
					},
					[]tclass{},
				},
			},
		},
	},

	{
		"array float attributes", "arr[]={1.2,2.3,3.4};",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{
				{"arr", TFloat, []string{"1.2", "2.3", "3.4"}},
			},
			[]tclass{},
		},
	},

	{
		"subclass array float attributes", "class test { arr[]={1.2,2.3,3.4}; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"test",
					[]Property{},
					[]ArrayProperty{
						{"arr", TFloat, []string{"1.2", "2.3", "3.4"}},
					},
					[]tclass{},
				},
			},
		},
	},

	{
		"array string attributes", "arr[]={\"a\",\"b\",\"c\"};",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{
				{"arr", TString, []string{"a", "b", "c"}},
			},
			[]tclass{},
		},
	},

	{
		"subclass array string attributes", "class test { arr[]={\"a\",\"b\",\"c\"}; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"test",
					[]Property{},
					[]ArrayProperty{
						{"arr", TString, []string{"a", "b", "c"}},
					},
					[]tclass{},
				},
			},
		},
	},
}

func TestParse(t *testing.T) {
	for _, test := range parseTests {
		p := MakeParser(test.input)
		c, err := p.Run()
		if err != nil {
			t.Errorf("Parser returned with error %q", err)
			continue
		}
		testClass(t, test.class, c)

	}
}

func TestParseSimple(t *testing.T) {
	p := MakeParser("class testclass { version=11; };")
	c, err := p.Run()
	if err != nil {
		t.Errorf("Parser returned with error %q", err)
	}
	if len(c.Classes) != 1 {
		t.Errorf("Class not processed")
	}
	tc := c.Classes[0]
	if tc.Name != "testclass" {
		t.Errorf("Wrong class name")
	}
	if len(tc.Props) != 1 {
		t.Errorf("Attribute not processed")
	}
	if tc.Arrprops != nil && len(tc.Arrprops) > 0 {
		t.Errorf("Array props are not empty")
	}
	at := tc.Props[0]
	if at.Name != "version" {
		t.Errorf("Prop wrong identifier")
	}
	if at.Typ != TInt {
		t.Errorf("Type of prop wrong")
	}
	if at.Value != "11" {
		t.Errorf("Wrong prop value")
	}

}

func testClass(t *testing.T, tclass tclass, class *Class) {
	if tclass.name != class.Name {
		t.Errorf("Classname is %s but should be %s", class.Name, tclass.name)
		return
	}
	if (class.Arrprops == nil && len(tclass.arrprops) > 0) || (class.Arrprops != nil && len(tclass.props) != len(class.Props)) {
		t.Errorf("Class %s Prop length is %d but should be %d", tclass.name, len(class.Props), len(tclass.props))
		return
	}
	if (class.Arrprops == nil && len(tclass.arrprops) > 0) || (class.Arrprops != nil && len(tclass.arrprops) != len(class.Arrprops)) {
		t.Errorf("Class %s Prop length is %d but should be %d", tclass.name, len(class.Props), len(tclass.props))
		return
	}
	if (class.Classes == nil && len(tclass.classes) > 0) || (class.Classes != nil && len(tclass.classes) != len(class.Classes)) {
		t.Errorf("Class %s Classes length is %d but should be %d", tclass.name, len(class.Classes), len(tclass.classes))
		return
	}
	for i, tprop := range tclass.props {
		prop := class.Props[i]
		if tprop.Name != prop.Name {
			t.Errorf("Class %s Propname %s but should be %s", tclass.name, prop.Name, tprop.Name)
			return
		}
		if tprop.Typ != prop.Typ {
			t.Errorf("Class %s Proptype %s but should be %s", tclass.name, prop.Typ, tprop.Typ)
			return
		}
		if tprop.Value != prop.Value {
			t.Errorf("Class %s Propvalue %s but should be %s", tclass.name, prop.Value, tprop.Value)
			return
		}
	}
	for i, tprop := range tclass.arrprops {
		prop := class.Arrprops[i]
		if tprop.Name != prop.Name {
			t.Errorf("Class %s ArrPropname %s but should be %s", tclass.name, prop.Name, tprop.Name)
			return
		}
		if tprop.Typ != prop.Typ {
			t.Errorf("Class %s ArrProptype %s but should be %s", tclass.name, prop.Typ, tprop.Typ)
			return
		}
		for j, tval := range tprop.Values {
			val := prop.Values[j]
			if tval != val {
				t.Errorf("Class %s ArrPropvalue %s but should be %s", tclass.name, val, tval)
				return
			}
		}
	}
	for k, tsubc := range tclass.classes {
		subc := class.Classes[k]
		testClass(t, tsubc, subc)
	}

}

func TestParseMissionSQM(t *testing.T) {
	const name = "Mission.sqm parser"
	if testing.Short() {
		t.Skip("Skip mission.sqm in short mode")
		return
	}
	buf, err := ioutil.ReadFile("./mission.sqm")
	if err != nil {
		t.Errorf("Could not open mission.sqm")
		return
	}
	p := MakeParser(string(buf))
	// c, perr := p.Run()
	_, perr := p.Run()
	if perr != nil {
		t.Errorf("Parser returned with error %q", perr)
	}
	//t.Logf("Class parsed: %q", c)
}

func BenchmarkParseMissionSQM(b *testing.B) {
	buf, err := ioutil.ReadFile("./mission.sqm")
	bufstr := string(buf)
	if err != nil {
		b.Errorf("Could not open mission.sqm")
		return
	}
	for n := 0; n < b.N; n++ {
		p := MakeParser(bufstr)
		// c, perr := p.Run()
		_, perr := p.Run()
		if perr != nil {
			b.Errorf("Parser returned with error %q", perr)
		}
	}
}
<commit_msg>Added benchmark<commit_after>package sqm

import (
	"io/ioutil"
	"testing"
)

func TestStructure(t *testing.T) {
	class := &Class{Name: "mission"}
	if class.Props != nil {
		t.Errorf("Class has props")
	}
	if len(class.Props) > 0 {
		t.Errorf("Props length is %d", len(class.Props))
	}
	class.Props = append(class.Props, &Property{"test", TInt, "value"})
	if class.Props == nil {
		t.Errorf("Class has no props")
	}

	if len(class.Props) != 1 {
		t.Errorf("Props length is %d", len(class.Props))
	}
}

type parseTest struct {
	name  string
	input string
	class tclass
}

type tclass struct {
	name     string
	props    []Property
	arrprops []ArrayProperty
	classes  []tclass
}

var parseTests = []parseTest{
	{
		"simple class", "class testclass { version=11; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"testclass",
					[]Property{
						{"version", TInt, "11"},
					},
					[]ArrayProperty{},
					[]tclass{},
				},
			},
		},
	},

	{
		"attributes", "version=11; string=\"teststring\"; float1=123.456; float2=+123.456;",
		tclass{"mission",
			[]Property{
				{"version", TInt, "11"},
				{"string", TString, "teststring"},
				{"float1", TFloat, "123.456"},
				{"float2", TFloat, "+123.456"},
			},
			[]ArrayProperty{},
			[]tclass{},
		},
	},

	{
		"array int attributes", "arr[]={1,2,3};",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{
				{"arr", TInt, []string{"1", "2", "3"}},
			},
			[]tclass{},
		},
	},

	{
		"subclass array int attributes", "class test { arr[]={1,2,3}; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"test",
					[]Property{},
					[]ArrayProperty{
						{"arr", TInt, []string{"1", "2", "3"}},
					},
					[]tclass{},
				},
			},
		},
	},

	{
		"array float attributes", "arr[]={1.2,2.3,3.4};",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{
				{"arr", TFloat, []string{"1.2", "2.3", "3.4"}},
			},
			[]tclass{},
		},
	},

	{
		"subclass array float attributes", "class test { arr[]={1.2,2.3,3.4}; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"test",
					[]Property{},
					[]ArrayProperty{
						{"arr", TFloat, []string{"1.2", "2.3", "3.4"}},
					},
					[]tclass{},
				},
			},
		},
	},

	{
		"array string attributes", "arr[]={\"a\",\"b\",\"c\"};",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{
				{"arr", TString, []string{"a", "b", "c"}},
			},
			[]tclass{},
		},
	},

	{
		"subclass array string attributes", "class test { arr[]={\"a\",\"b\",\"c\"}; };",
		tclass{"mission",
			[]Property{},
			[]ArrayProperty{},
			[]tclass{
				{"test",
					[]Property{},
					[]ArrayProperty{
						{"arr", TString, []string{"a", "b", "c"}},
					},
					[]tclass{},
				},
			},
		},
	},
}

func TestParse(t *testing.T) {
	for _, test := range parseTests {
		p := MakeParser(test.input)
		c, err := p.Run()
		if err != nil {
			t.Errorf("Parser returned with error %q", err)
			continue
		}
		testClass(t, test.class, c)

	}
}

func TestParseSimple(t *testing.T) {
	p := MakeParser("class testclass { version=11; };")
	c, err := p.Run()
	if err != nil {
		t.Errorf("Parser returned with error %q", err)
	}
	if len(c.Classes) != 1 {
		t.Errorf("Class not processed")
	}
	tc := c.Classes[0]
	if tc.Name != "testclass" {
		t.Errorf("Wrong class name")
	}
	if len(tc.Props) != 1 {
		t.Errorf("Attribute not processed")
	}
	if tc.Arrprops != nil && len(tc.Arrprops) > 0 {
		t.Errorf("Array props are not empty")
	}
	at := tc.Props[0]
	if at.Name != "version" {
		t.Errorf("Prop wrong identifier")
	}
	if at.Typ != TInt {
		t.Errorf("Type of prop wrong")
	}
	if at.Value != "11" {
		t.Errorf("Wrong prop value")
	}

}

func testClass(t *testing.T, tclass tclass, class *Class) {
	if tclass.name != class.Name {
		t.Errorf("Classname is %s but should be %s", class.Name, tclass.name)
		return
	}
	if (class.Arrprops == nil && len(tclass.arrprops) > 0) || (class.Arrprops != nil && len(tclass.props) != len(class.Props)) {
		t.Errorf("Class %s Prop length is %d but should be %d", tclass.name, len(class.Props), len(tclass.props))
		return
	}
	if (class.Arrprops == nil && len(tclass.arrprops) > 0) || (class.Arrprops != nil && len(tclass.arrprops) != len(class.Arrprops)) {
		t.Errorf("Class %s Prop length is %d but should be %d", tclass.name, len(class.Props), len(tclass.props))
		return
	}
	if (class.Classes == nil && len(tclass.classes) > 0) || (class.Classes != nil && len(tclass.classes) != len(class.Classes)) {
		t.Errorf("Class %s Classes length is %d but should be %d", tclass.name, len(class.Classes), len(tclass.classes))
		return
	}
	for i, tprop := range tclass.props {
		prop := class.Props[i]
		if tprop.Name != prop.Name {
			t.Errorf("Class %s Propname %s but should be %s", tclass.name, prop.Name, tprop.Name)
			return
		}
		if tprop.Typ != prop.Typ {
			t.Errorf("Class %s Proptype %s but should be %s", tclass.name, prop.Typ, tprop.Typ)
			return
		}
		if tprop.Value != prop.Value {
			t.Errorf("Class %s Propvalue %s but should be %s", tclass.name, prop.Value, tprop.Value)
			return
		}
	}
	for i, tprop := range tclass.arrprops {
		prop := class.Arrprops[i]
		if tprop.Name != prop.Name {
			t.Errorf("Class %s ArrPropname %s but should be %s", tclass.name, prop.Name, tprop.Name)
			return
		}
		if tprop.Typ != prop.Typ {
			t.Errorf("Class %s ArrProptype %s but should be %s", tclass.name, prop.Typ, tprop.Typ)
			return
		}
		for j, tval := range tprop.Values {
			val := prop.Values[j]
			if tval != val {
				t.Errorf("Class %s ArrPropvalue %s but should be %s", tclass.name, val, tval)
				return
			}
		}
	}
	for k, tsubc := range tclass.classes {
		subc := class.Classes[k]
		testClass(t, tsubc, subc)
	}

}

func TestParseMissionSQM(t *testing.T) {
	const name = "Mission.sqm parser"
	if testing.Short() {
		t.Skip("Skip mission.sqm in short mode")
		return
	}
	buf, err := ioutil.ReadFile("./mission.sqm")
	if err != nil {
		t.Errorf("Could not open mission.sqm")
		return
	}
	p := MakeParser(string(buf))
	// c, perr := p.Run()
	_, perr := p.Run()
	if perr != nil {
		t.Errorf("Parser returned with error %q", perr)
	}
	//t.Logf("Class parsed: %q", c)
}

func BenchmarkParseMissionSQM(b *testing.B) {
	buf, err := ioutil.ReadFile("./mission.sqm")
	bufstr := string(buf)
	if err != nil {
		b.Errorf("Could not open mission.sqm")
		return
	}
	for n := 0; n < b.N; n++ {
		p := MakeParser(bufstr)
		// c, perr := p.Run()
		_, perr := p.Run()
		if perr != nil {
			b.Errorf("Parser returned with error %q", perr)
		}
	}
}

func BenchmarkParseMissionSQMReadfile(b *testing.B) {

	for n := 0; n < b.N; n++ {
		buf, err := ioutil.ReadFile("./mission.sqm")
		bufstr := string(buf)
		if err != nil {
			b.Errorf("Could not open mission.sqm")
			return
		}
		p := MakeParser(bufstr)
		// c, perr := p.Run()
		_, perr := p.Run()
		if perr != nil {
			b.Errorf("Parser returned with error %q", perr)
		}
	}
}
<|endoftext|>