<commit_before>package ginprometheus

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"
)

var defaultMetricPath = "/metrics"

// Prometheus contains the metrics gathered by the instance and its path
type Prometheus struct {
	reqCnt               *prometheus.CounterVec
	reqDur, reqSz, resSz prometheus.Summary
	router               *gin.Engine
	listenAddress        string

	Ppg PrometheusPushGateway

	MetricsPath string
}

// PrometheusPushGateway contains the configuration for pushing to a Prometheus pushgateway (optional)
type PrometheusPushGateway struct {

	// Push interval in seconds
	PushIntervalSeconds time.Duration

	// Push Gateway URL in format http://domain:port
	// where JOBNAME can be any string of your choice
	PushGatewayURL string

	// Local metrics URL where metrics are fetched from, this could be ommited in the future
	// if implemented using prometheus common/expfmt instead
	MetricsURL string

	// pushgateway job name, defaults to "gin"
	Job string
}

// NewPrometheus generates a new set of metrics with a certain subsystem name
func NewPrometheus(subsystem string) *Prometheus {

	p := &Prometheus{
		MetricsPath: defaultMetricPath,
	}
	p.registerMetrics(subsystem)

	return p
}

// SetPushGateway sends metrics to a remote pushgateway exposed on pushGatewayURL
// every pushIntervalSeconds. Metrics are fetched from metricsURL
func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushIntervalSeconds time.Duration) {
	p.Ppg.PushGatewayURL = pushGatewayURL
	p.Ppg.MetricsURL = metricsURL
	p.Ppg.PushIntervalSeconds = pushIntervalSeconds
	p.startPushTicker()
}

// Set pushgateway job name, defaults to "gin"
func (p *Prometheus) SetPushGatewayJob(j string) {
	p.Ppg.Job = j
}

// SetListenAddress for exposing metrics on address. If not set, it will be exposed at the
// same address of the gin engine that is being used
func (p *Prometheus) SetListenAddress(address string) {
	p.listenAddress = address
	if p.listenAddress != "" {
		p.router = gin.Default()
	}
}

func (p *Prometheus) setMetricsPath(e *gin.Engine) {

	if p.listenAddress != "" {
		p.router.GET(p.MetricsPath, prometheusHandler())
		p.runServer()
	} else {
		e.GET(p.MetricsPath, prometheusHandler())
	}
}

func (p *Prometheus) setMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts) {

	if p.listenAddress != "" {
		p.router.GET(p.MetricsPath, gin.BasicAuth(accounts), prometheusHandler())
		p.runServer()
	} else {
		e.GET(p.MetricsPath, gin.BasicAuth(accounts), prometheusHandler())
	}

}

func (p *Prometheus) runServer() {
	if p.listenAddress != "" {
		go p.router.Run(p.listenAddress)
	}
}

func (p *Prometheus) getMetrics() []byte {
	response, _ := http.Get(p.Ppg.MetricsURL)

	defer response.Body.Close()
	body, _ := ioutil.ReadAll(response.Body)

	return body
}

func (p *Prometheus) getPushGatewayUrl() string {
	h, _ := os.Hostname()
	if p.Ppg.Job == "" {
		p.Ppg.Job = "gin"
	}
	return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + h
}

func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) {
	req, err := http.NewRequest("POST", p.getPushGatewayUrl(), bytes.NewBuffer(metrics))
	client := &http.Client{}
	_, err = client.Do(req)
	if err != nil {
		log.Error("Error sending to push gatway: " + err.Error())
	}
}

func (p *Prometheus) startPushTicker() {
	ticker := time.NewTicker(time.Second * p.Ppg.PushIntervalSeconds)
	go func() {
		for range ticker.C {
			p.sendMetricsToPushGateway(p.getMetrics())
		}
	}()
}

func (p *Prometheus) registerMetrics(subsystem string) {

	p.reqCnt = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Subsystem: subsystem,
			Name:      "requests_total",
			Help:      "How many HTTP requests processed, partitioned by status code and HTTP method.",
		},
		[]string{"code", "method", "handler", "host"},
	)

	if err := prometheus.Register(p.reqCnt); err != nil {
		log.Info("reqCnt could not be registered: ", err)
	} else {
		log.Info("reqCnt registered.")
	}

	p.reqDur = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Subsystem: subsystem,
			Name:      "request_duration_seconds",
			Help:      "The HTTP request latencies in seconds.",
		},
	)

	if err := prometheus.Register(p.reqDur); err != nil {
		log.Info("reqDur could not be registered: ", err)
	} else {
		log.Info("reqDur registered.")
	}

	p.reqSz = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Subsystem: subsystem,
			Name:      "request_size_bytes",
			Help:      "The HTTP request sizes in bytes.",
		},
	)

	if err := prometheus.Register(p.reqSz); err != nil {
		log.Info("reqSz could not be registered: ", err)
	} else {
		log.Info("reqSz registered.")
	}

	p.resSz = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Subsystem: subsystem,
			Name:      "response_size_bytes",
			Help:      "The HTTP response sizes in bytes.",
		},
	)

	if err := prometheus.Register(p.resSz); err != nil {
		log.Info("resSz could not be registered: ", err)
	} else {
		log.Info("resSz registered.")
	}

}

// Use adds the middleware to a gin engine.
func (p *Prometheus) Use(e *gin.Engine) {
	e.Use(p.handlerFunc())
	p.setMetricsPath(e)
}

// UseWithAuth adds the middleware to a gin engine with BasicAuth.
func (p *Prometheus) UseWithAuth(e *gin.Engine, accounts gin.Accounts) {
	e.Use(p.handlerFunc())
	p.setMetricsPathWithAuth(e, accounts)
}

func (p *Prometheus) handlerFunc() gin.HandlerFunc {
	return func(c *gin.Context) {
		if c.Request.URL.String() == p.MetricsPath {
			c.Next()
			return
		}

		start := time.Now()
		reqSz := computeApproximateRequestSize(c.Request)

		c.Next()

		status := strconv.Itoa(c.Writer.Status())
		elapsed := float64(time.Since(start)) / float64(time.Second)
		resSz := float64(c.Writer.Size())

		p.reqDur.Observe(elapsed)
		p.reqCnt.WithLabelValues(status, c.Request.Method, c.HandlerName(), c.Request.Host).Inc()
		p.reqSz.Observe(float64(reqSz))
		p.resSz.Observe(resSz)
	}
}

func prometheusHandler() gin.HandlerFunc {
	h := promhttp.Handler()
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// From https://github.com/DanielHeckrath/gin-prometheus/blob/master/gin_prometheus.go
func computeApproximateRequestSize(r *http.Request) int {
	s := 0
	if r.URL != nil {
		s = len(r.URL.String())
	}

	s += len(r.Method)
	s += len(r.Proto)
	for name, values := range r.Header {
		s += len(name)
		for _, value := range values {
			s += len(value)
		}
	}
	s += len(r.Host)

	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.

	if r.ContentLength != -1 {
		s += int(r.ContentLength)
	}
	return s
}
<commit_msg>use proper capitalization of logrus<commit_after>package ginprometheus

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/Sirupsen/logrus"
)

var defaultMetricPath = "/metrics"

// Prometheus contains the metrics gathered by the instance and its path
type Prometheus struct {
	reqCnt               *prometheus.CounterVec
	reqDur, reqSz, resSz prometheus.Summary
	router               *gin.Engine
	listenAddress        string

	Ppg PrometheusPushGateway

	MetricsPath string
}

// PrometheusPushGateway contains the configuration for pushing to a Prometheus pushgateway (optional)
type PrometheusPushGateway struct {

	// Push interval in seconds
	PushIntervalSeconds time.Duration

	// Push Gateway URL in format http://domain:port
	// where JOBNAME can be any string of your choice
	PushGatewayURL string

	// Local metrics URL where metrics are fetched from, this could be ommited in the future
	// if implemented using prometheus common/expfmt instead
	MetricsURL string

	// pushgateway job name, defaults to "gin"
	Job string
}

// NewPrometheus generates a new set of metrics with a certain subsystem name
func NewPrometheus(subsystem string) *Prometheus {

	p := &Prometheus{
		MetricsPath: defaultMetricPath,
	}
	p.registerMetrics(subsystem)

	return p
}

// SetPushGateway sends metrics to a remote pushgateway exposed on pushGatewayURL
// every pushIntervalSeconds. Metrics are fetched from metricsURL
func (p *Prometheus) SetPushGateway(pushGatewayURL, metricsURL string, pushIntervalSeconds time.Duration) {
	p.Ppg.PushGatewayURL = pushGatewayURL
	p.Ppg.MetricsURL = metricsURL
	p.Ppg.PushIntervalSeconds = pushIntervalSeconds
	p.startPushTicker()
}

// Set pushgateway job name, defaults to "gin"
func (p *Prometheus) SetPushGatewayJob(j string) {
	p.Ppg.Job = j
}

// SetListenAddress for exposing metrics on address. If not set, it will be exposed at the
// same address of the gin engine that is being used
func (p *Prometheus) SetListenAddress(address string) {
	p.listenAddress = address
	if p.listenAddress != "" {
		p.router = gin.Default()
	}
}

func (p *Prometheus) setMetricsPath(e *gin.Engine) {

	if p.listenAddress != "" {
		p.router.GET(p.MetricsPath, prometheusHandler())
		p.runServer()
	} else {
		e.GET(p.MetricsPath, prometheusHandler())
	}
}

func (p *Prometheus) setMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts) {

	if p.listenAddress != "" {
		p.router.GET(p.MetricsPath, gin.BasicAuth(accounts), prometheusHandler())
		p.runServer()
	} else {
		e.GET(p.MetricsPath, gin.BasicAuth(accounts), prometheusHandler())
	}

}

func (p *Prometheus) runServer() {
	if p.listenAddress != "" {
		go p.router.Run(p.listenAddress)
	}
}

func (p *Prometheus) getMetrics() []byte {
	response, _ := http.Get(p.Ppg.MetricsURL)

	defer response.Body.Close()
	body, _ := ioutil.ReadAll(response.Body)

	return body
}

func (p *Prometheus) getPushGatewayUrl() string {
	h, _ := os.Hostname()
	if p.Ppg.Job == "" {
		p.Ppg.Job = "gin"
	}
	return p.Ppg.PushGatewayURL + "/metrics/job/" + p.Ppg.Job + "/instance/" + h
}

func (p *Prometheus) sendMetricsToPushGateway(metrics []byte) {
	req, err := http.NewRequest("POST", p.getPushGatewayUrl(), bytes.NewBuffer(metrics))
	client := &http.Client{}
	_, err = client.Do(req)
	if err != nil {
		log.Error("Error sending to push gatway: " + err.Error())
	}
}

func (p *Prometheus) startPushTicker() {
	ticker := time.NewTicker(time.Second * p.Ppg.PushIntervalSeconds)
	go func() {
		for range ticker.C {
			p.sendMetricsToPushGateway(p.getMetrics())
		}
	}()
}

func (p *Prometheus) registerMetrics(subsystem string) {

	p.reqCnt = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Subsystem: subsystem,
			Name:      "requests_total",
			Help:      "How many HTTP requests processed, partitioned by status code and HTTP method.",
		},
		[]string{"code", "method", "handler", "host"},
	)

	if err := prometheus.Register(p.reqCnt); err != nil {
		log.Info("reqCnt could not be registered: ", err)
	} else {
		log.Info("reqCnt registered.")
	}

	p.reqDur = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Subsystem: subsystem,
			Name:      "request_duration_seconds",
			Help:      "The HTTP request latencies in seconds.",
		},
	)

	if err := prometheus.Register(p.reqDur); err != nil {
		log.Info("reqDur could not be registered: ", err)
	} else {
		log.Info("reqDur registered.")
	}

	p.reqSz = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Subsystem: subsystem,
			Name:      "request_size_bytes",
			Help:      "The HTTP request sizes in bytes.",
		},
	)

	if err := prometheus.Register(p.reqSz); err != nil {
		log.Info("reqSz could not be registered: ", err)
	} else {
		log.Info("reqSz registered.")
	}

	p.resSz = prometheus.NewSummary(
		prometheus.SummaryOpts{
			Subsystem: subsystem,
			Name:      "response_size_bytes",
			Help:      "The HTTP response sizes in bytes.",
		},
	)

	if err := prometheus.Register(p.resSz); err != nil {
		log.Info("resSz could not be registered: ", err)
	} else {
		log.Info("resSz registered.")
	}

}

// Use adds the middleware to a gin engine.
func (p *Prometheus) Use(e *gin.Engine) {
	e.Use(p.handlerFunc())
	p.setMetricsPath(e)
}

// UseWithAuth adds the middleware to a gin engine with BasicAuth.
func (p *Prometheus) UseWithAuth(e *gin.Engine, accounts gin.Accounts) {
	e.Use(p.handlerFunc())
	p.setMetricsPathWithAuth(e, accounts)
}

func (p *Prometheus) handlerFunc() gin.HandlerFunc {
	return func(c *gin.Context) {
		if c.Request.URL.String() == p.MetricsPath {
			c.Next()
			return
		}

		start := time.Now()
		reqSz := computeApproximateRequestSize(c.Request)

		c.Next()

		status := strconv.Itoa(c.Writer.Status())
		elapsed := float64(time.Since(start)) / float64(time.Second)
		resSz := float64(c.Writer.Size())

		p.reqDur.Observe(elapsed)
		p.reqCnt.WithLabelValues(status, c.Request.Method, c.HandlerName(), c.Request.Host).Inc()
		p.reqSz.Observe(float64(reqSz))
		p.resSz.Observe(resSz)
	}
}

func prometheusHandler() gin.HandlerFunc {
	h := promhttp.Handler()
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// From https://github.com/DanielHeckrath/gin-prometheus/blob/master/gin_prometheus.go
func computeApproximateRequestSize(r *http.Request) int {
	s := 0
	if r.URL != nil {
		s = len(r.URL.String())
	}

	s += len(r.Method)
	s += len(r.Proto)
	for name, values := range r.Header {
		s += len(name)
		for _, value := range values {
			s += len(value)
		}
	}
	s += len(r.Host)

	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.

	if r.ContentLength != -1 {
		s += int(r.ContentLength)
	}
	return s
}
<|endoftext|>


<commit_before>package powermux

import (
	"net/http"
)

// NextMiddlewareFunc is the function signature for the next parameter in ServerHTTPMiddleware
type NextMiddlewareFunc func(http.ResponseWriter, *http.Request)

//The MiddlewareFunc type is an adapter to allow the use of ordinary functions as HTTP middlewares.
//
// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler that calls f.
type MiddlewareFunc func(http.ResponseWriter, *http.Request, NextMiddlewareFunc)

// ServeHTTPMiddleware calls f(w, r, n).
func (m MiddlewareFunc) ServeHTTPMiddleware(rw http.ResponseWriter, req *http.Request, n NextMiddlewareFunc) {
	m(rw, req, n)
}

// Middleware handles HTTP requests and optionally passes them along to the next handler in the chain.
type Middleware interface {
	ServeHTTPMiddleware(http.ResponseWriter, *http.Request, NextMiddlewareFunc)
}

// getNextMiddleware returns the first middleware of a recursive closure.
// The returned middleware will have the next middleware in the array available to it as a parameter
// and the last middleware will have the final handler.
func getNextMiddleware(mids []Middleware, h http.Handler) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if len(mids) > 0 {
			mids[0].ServeHTTPMiddleware(w, r, getNextMiddleware(mids[1:], h))
		} else {
			h.ServeHTTP(w, r)
		}
	}
}
<commit_msg>Removed function type<commit_after>package powermux

import (
	"net/http"
)

//The MiddlewareFunc type is an adapter to allow the use of ordinary functions as HTTP middlewares.
//
// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler that calls f.
type MiddlewareFunc func(http.ResponseWriter, *http.Request, func(http.ResponseWriter, *http.Request))

// ServeHTTPMiddleware calls f(w, r, n).
func (m MiddlewareFunc) ServeHTTPMiddleware(rw http.ResponseWriter, req *http.Request, n func(http.ResponseWriter, *http.Request)) {
	m(rw, req, n)
}

// Middleware handles HTTP requests and optionally passes them along to the next handler in the chain.
type Middleware interface {
	ServeHTTPMiddleware(http.ResponseWriter, *http.Request, func(http.ResponseWriter, *http.Request))
}

// getNextMiddleware returns the first middleware of a recursive closure.
// The returned middleware will have the next middleware in the array available to it as a parameter
// and the last middleware will have the final handler.
func getNextMiddleware(mids []Middleware, h http.Handler) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if len(mids) > 0 {
			mids[0].ServeHTTPMiddleware(w, r, getNextMiddleware(mids[1:], h))
		} else {
			h.ServeHTTP(w, r)
		}
	}
}
<|endoftext|>


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

// +build !linux

package ode

func (o *Solver) init_mpi() {
}

func radau5_step_mpi(o *Solver, y0 []float64, x0 float64) (rerr float64, err error) {
	return
}
<commit_msg>Remove unused file<commit_after><|endoftext|>


<commit_before>package dispatcher

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"testing"

	"github.com/qa-dev/universe/event"
	"github.com/qa-dev/universe/plugins"
	"github.com/qa-dev/universe/queue"
	"github.com/qa-dev/universe/rabbitmq"
	"github.com/qa-dev/universe/subscribe"
	"github.com/stretchr/testify/assert"
)

var amqpUri string

func init() {
	amqpUri = os.Getenv("AMQP_URI")
	if amqpUri == "" {
		amqpUri = "amqp://guest:guest@127.0.0.1:5672/"
	}
}

type FakeClosingBuffer struct {
	*bytes.Buffer
}

func (cb *FakeClosingBuffer) Close() error {
	return nil
}

type FakePostClient struct {
	t                   *testing.T
	ExpectedRequestUrl  string
	ExpectedRequestData []byte
}

func (c *FakePostClient) Do(r *http.Request) (*http.Response, error) {
	body, err := ioutil.ReadAll(r.Body)
	assert.NoError(c.t, err)
	assert.Equal(c.t, c.ExpectedRequestUrl, r.URL.String())
	assert.Equal(c.t, c.ExpectedRequestData, body)
	closingBuffer := &FakeClosingBuffer{bytes.NewBufferString("Hi!")}
	var readCloser io.ReadCloser
	readCloser = closingBuffer
	response := &http.Response{}
	response.Status = "200 OK"
	response.StatusCode = 200
	response.Body = readCloser
	return response, nil
}

func TestNewDispatcher(t *testing.T) {
	rmq := rabbitmq.NewRabbitMQ(amqpUri, "test_new_dispatcher")
	q := queue.NewQueue(rmq)
	storage := plugins.NewPluginStorage()
	dsp := NewDispatcher(q, storage)
	assert.Equal(t, fmt.Sprintf("%p", q), fmt.Sprintf("%p", dsp.queue))
}

func TestDispatcher_Run(t *testing.T) {
	rmq := rabbitmq.NewRabbitMQ(amqpUri, "test_new_dispatcher")
	q := queue.NewQueue(rmq)
	storage := plugins.NewPluginStorage()
	requestData := []byte(`{"test": "test"}`)
	subscrService := subscribe.NewSubscribeService(storage)
	eventService := event.NewEventService(q)
	subscribeData := []byte(`{"test": "hello"}`)
	subscrService.ProcessSubscribe("log", subscribeData)
	dsp := NewDispatcher(q, storage)
	assert.NotNil(t, dsp)
	dsp.Run()
	err := eventService.Publish(&event.Event{"test.event", requestData})
	assert.NoError(t, err)
	// TODO: assert log
}

// Disabled
//
//func TestDispatcher_Run_Negative(t *testing.T) {
//	requestUrl := "test_url"
//	requestData := []byte("{\"test\": \"test\"}")
//	ch := make(chan data.Event)
//	storage := data.NewStorage()
//	subscrService := subscribe.NewSubscribeService(storage)
//	eventService := NewEventService(ch)
//	subscribeData := subscribe.Subscribe{EventName: "test.event", WebHookPath: requestUrl}
//	subscrService.ProcessSubscribe(subscribeData)
//	client := &FakePostClient{t, requestUrl, requestData}
//	dsp := NewDispatcher(ch, storage, client)
//	assert.NotNil(t, dsp)
//	dsp.Run()
//	err := eventService.PushEvent("test.event", requestData)
//	assert.NoError(t, err)
//
//	var buf bytes.Buffer
//	log.SetOutput(&buf)
//
//	event := data.Event{Name: "", Payload: []byte("")}
//	ch <- event
//
//	log.SetOutput(os.Stderr)
//	logVal := buf.String()
//
//	time.Sleep(1000)
//
//	assert.Contains(t, logVal, "No subscribers for event")
//}
<commit_msg>mongo+rabbit<commit_after>package dispatcher

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"testing"

	"github.com/qa-dev/universe/event"
	"github.com/qa-dev/universe/plugins"
	"github.com/qa-dev/universe/queue"
	"github.com/qa-dev/universe/rabbitmq"
	"github.com/qa-dev/universe/subscribe"
	"github.com/stretchr/testify/assert"
	"time"
)

var amqpUri string

func init() {
	amqpUri = os.Getenv("AMQP_URI")
	if amqpUri == "" {
		amqpUri = "amqp://guest:guest@127.0.0.1:5672/"
	}
}

type FakeClosingBuffer struct {
	*bytes.Buffer
}

func (cb *FakeClosingBuffer) Close() error {
	return nil
}

type FakePostClient struct {
	t                   *testing.T
	ExpectedRequestUrl  string
	ExpectedRequestData []byte
}

func (c *FakePostClient) Do(r *http.Request) (*http.Response, error) {
	body, err := ioutil.ReadAll(r.Body)
	assert.NoError(c.t, err)
	assert.Equal(c.t, c.ExpectedRequestUrl, r.URL.String())
	assert.Equal(c.t, c.ExpectedRequestData, body)
	closingBuffer := &FakeClosingBuffer{bytes.NewBufferString("Hi!")}
	var readCloser io.ReadCloser
	readCloser = closingBuffer
	response := &http.Response{}
	response.Status = "200 OK"
	response.StatusCode = 200
	response.Body = readCloser
	return response, nil
}

func TestNewDispatcher(t *testing.T) {
	rmq := rabbitmq.NewRabbitMQ(amqpUri, "test_new_dispatcher")
	time.Sleep(2 * time.Second)
	q := queue.NewQueue(rmq)
	storage := plugins.NewPluginStorage()
	dsp := NewDispatcher(q, storage)
	assert.Equal(t, fmt.Sprintf("%p", q), fmt.Sprintf("%p", dsp.queue))
}

func TestDispatcher_Run(t *testing.T) {
	rmq := rabbitmq.NewRabbitMQ(amqpUri, "test_new_dispatcher")
	time.Sleep(2 * time.Second)
	q := queue.NewQueue(rmq)
	storage := plugins.NewPluginStorage()
	requestData := []byte(`{"test": "test"}`)
	subscrService := subscribe.NewSubscribeService(storage)
	eventService := event.NewEventService(q)
	subscribeData := []byte(`{"test": "hello"}`)
	subscrService.ProcessSubscribe("log", subscribeData)
	dsp := NewDispatcher(q, storage)
	assert.NotNil(t, dsp)
	dsp.Run()
	err := eventService.Publish(&event.Event{"test.event", requestData})
	assert.NoError(t, err)
	// TODO: assert log
}

// Disabled
//
//func TestDispatcher_Run_Negative(t *testing.T) {
//	requestUrl := "test_url"
//	requestData := []byte("{\"test\": \"test\"}")
//	ch := make(chan data.Event)
//	storage := data.NewStorage()
//	subscrService := subscribe.NewSubscribeService(storage)
//	eventService := NewEventService(ch)
//	subscribeData := subscribe.Subscribe{EventName: "test.event", WebHookPath: requestUrl}
//	subscrService.ProcessSubscribe(subscribeData)
//	client := &FakePostClient{t, requestUrl, requestData}
//	dsp := NewDispatcher(ch, storage, client)
//	assert.NotNil(t, dsp)
//	dsp.Run()
//	err := eventService.PushEvent("test.event", requestData)
//	assert.NoError(t, err)
//
//	var buf bytes.Buffer
//	log.SetOutput(&buf)
//
//	event := data.Event{Name: "", Payload: []byte("")}
//	ch <- event
//
//	log.SetOutput(os.Stderr)
//	logVal := buf.String()
//
//	time.Sleep(1000)
//
//	assert.Contains(t, logVal, "No subscribers for event")
//}
<|endoftext|>


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

package ebiten

import (
	"bytes"
	"fmt"
	"go/parser"
	"go/token"
	"strings"

	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/mipmap"
	"github.com/hajimehoshi/ebiten/internal/shader"
	"github.com/hajimehoshi/ebiten/internal/shaderir"
)

var shaderSuffix string

func init() {
	shaderSuffix = `
var __imageDstTextureSize vec2

// imageSrcTextureSize returns the destination image's texture size in pixels.
func imageDstTextureSize() vec2 {
	return __imageDstTextureSize
}
`

	shaderSuffix += fmt.Sprintf(`
var __textureSizes [%[1]d]vec2

// imageSrcTextureSize returns the source image's texture size in pixels.
// As an image is a part of internal texture, the texture is usually bigger than the image.
// The texture's size is useful when you want to calculate pixels from texels.
func imageSrcTextureSize() vec2 {
	return __textureSizes[0]
}

// The unit is the source texture's texel.
var __textureSourceOffsets [%[2]d]vec2

// The unit is the source texture's texel.
var __textureSourceRegionOrigin vec2

// The unit is the source texture's texel.
var __textureSourceRegionSize vec2

// imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture.
// The unit is the source texture's texel.
//
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
func imageSrcRegionOnTexture() (vec2, vec2) {
	return __textureSourceRegionOrigin, __textureSourceRegionSize
}
`, graphics.ShaderImageNum, graphics.ShaderImageNum-1)

	for i := 0; i < graphics.ShaderImageNum; i++ {
		pos := "pos"
		if i >= 1 {
			// Convert the position in texture0's texels to the target texture texels.
			pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i)
		}
		// __t%d is a special variable for a texture variable.
		shaderSuffix += fmt.Sprintf(`
func imageSrc%[1]dUnsafeAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s)
}

func imageSrc%[1]dAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s) *
		step(__textureSourceRegionOrigin.x, pos.x) *
		(1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) *
		step(__textureSourceRegionOrigin.y, pos.y) *
		(1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y))
}
`, i, pos)
	}

	shaderSuffix += `
func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
	return mat4(
		2/__imageDstTextureSize.x, 0, 0, 0,
		0, 2/__imageDstTextureSize.y, 0, 0,
		0, 0, 1, 0,
		-1, -1, 0, 1,
	) * vec4(position, 0, 1), texCoord, color
}
`
}

type Shader struct {
	shader       *mipmap.Shader
	uniformNames []string
	uniformTypes []shaderir.Type
}

func NewShader(src []byte) (*Shader, error) {
	var buf bytes.Buffer
	buf.Write(src)
	buf.WriteString(shaderSuffix)

	fs := token.NewFileSet()
	f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors)
	if err != nil {
		return nil, err
	}

	const (
		vert = "__vertex"
		frag = "Fragment"
	)
	s, err := shader.Compile(fs, f, vert, frag, graphics.ShaderImageNum)
	if err != nil {
		return nil, err
	}

	if s.VertexFunc.Block == nil {
		return nil, fmt.Errorf("ebiten: vertex shader entry point '%s' is missing", vert)
	}
	if s.FragmentFunc.Block == nil {
		return nil, fmt.Errorf("ebiten: fragment shader entry point '%s' is missing", frag)
	}

	return &Shader{
		shader:       mipmap.NewShader(s),
		uniformNames: s.UniformNames,
		uniformTypes: s.Uniforms,
	}, nil
}

func (s *Shader) Dispose() {
	s.shader.MarkDisposed()
	s.shader = nil
}

func (s *Shader) convertUniforms(uniforms map[string]interface{}) []interface{} {
	type index struct {
		resultIndex        int
		shaderUniformIndex int
	}

	names := map[string]index{}
	var idx int
	for i, n := range s.uniformNames {
		if strings.HasPrefix(n, "__") {
			continue
		}
		names[n] = index{
			resultIndex:        idx,
			shaderUniformIndex: i,
		}
		idx++
	}

	us := make([]interface{}, len(names))
	for name, idx := range names {
		if v, ok := uniforms[name]; ok {
			// TODO: Check the uniform variable types?
			us[idx.resultIndex] = v
			continue
		}

		t := s.uniformTypes[idx.shaderUniformIndex]
		v := zeroUniformValue(t)
		if v == nil {
			panic(fmt.Sprintf("ebiten: unexpected uniform variable type: %s", t.String()))
		}
		us[idx.resultIndex] = v
	}

	// TODO: Panic if uniforms include an invalid name

	return us
}

func zeroUniformValue(t shaderir.Type) interface{} {
	switch t.Main {
	case shaderir.Bool:
		return false
	case shaderir.Int:
		return 0
	case shaderir.Float:
		return float32(0)
	case shaderir.Array:
		switch t.Sub[0].Main {
		case shaderir.Bool:
			return make([]bool, t.Length)
		case shaderir.Int:
			return make([]int, t.Length)
		default:
			return make([]float32, t.FloatNum())
		}
	default:
		return make([]float32, t.FloatNum())
	}
}
<commit_msg>ebiten: Add comments about shaders<commit_after>// Copyright 2020 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"bytes"
	"fmt"
	"go/parser"
	"go/token"
	"strings"

	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/mipmap"
	"github.com/hajimehoshi/ebiten/internal/shader"
	"github.com/hajimehoshi/ebiten/internal/shaderir"
)

var shaderSuffix string

func init() {
	shaderSuffix = `
var __imageDstTextureSize vec2

// imageSrcTextureSize returns the destination image's texture size in pixels.
func imageDstTextureSize() vec2 {
	return __imageDstTextureSize
}
`

	shaderSuffix += fmt.Sprintf(`
var __textureSizes [%[1]d]vec2

// imageSrcTextureSize returns the source image's texture size in pixels.
// As an image is a part of internal texture, the texture is usually bigger than the image.
// The texture's size is useful when you want to calculate pixels from texels.
func imageSrcTextureSize() vec2 {
	return __textureSizes[0]
}

// The unit is the source texture's texel.
var __textureSourceOffsets [%[2]d]vec2

// The unit is the source texture's texel.
var __textureSourceRegionOrigin vec2

// The unit is the source texture's texel.
var __textureSourceRegionSize vec2

// imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture.
// The unit is the source texture's texel.
//
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
func imageSrcRegionOnTexture() (vec2, vec2) {
	return __textureSourceRegionOrigin, __textureSourceRegionSize
}
`, graphics.ShaderImageNum, graphics.ShaderImageNum-1)

	for i := 0; i < graphics.ShaderImageNum; i++ {
		pos := "pos"
		if i >= 1 {
			// Convert the position in texture0's texels to the target texture texels.
			pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i)
		}
		// __t%d is a special variable for a texture variable.
		shaderSuffix += fmt.Sprintf(`
func imageSrc%[1]dUnsafeAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s)
}

func imageSrc%[1]dAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s) *
		step(__textureSourceRegionOrigin.x, pos.x) *
		(1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) *
		step(__textureSourceRegionOrigin.y, pos.y) *
		(1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y))
}
`, i, pos)
	}

	shaderSuffix += `
func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
	return mat4(
		2/__imageDstTextureSize.x, 0, 0, 0,
		0, 2/__imageDstTextureSize.y, 0, 0,
		0, 0, 1, 0,
		-1, -1, 0, 1,
	) * vec4(position, 0, 1), texCoord, color
}
`
}

// Shader represents a compiled shader program.
//
// For the details about the shader, see https://ebiten.org/documents/shader.html.
type Shader struct {
	shader       *mipmap.Shader
	uniformNames []string
	uniformTypes []shaderir.Type
}

// NewShader compiles a shader program in the shading language Kage, and retruns the result.
//
// If the compilation fails, NewShader returns an error.
//
// For the details about the shader, see https://ebiten.org/documents/shader.html.
func NewShader(src []byte) (*Shader, error) {
	var buf bytes.Buffer
	buf.Write(src)
	buf.WriteString(shaderSuffix)

	fs := token.NewFileSet()
	f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors)
	if err != nil {
		return nil, err
	}

	const (
		vert = "__vertex"
		frag = "Fragment"
	)
	s, err := shader.Compile(fs, f, vert, frag, graphics.ShaderImageNum)
	if err != nil {
		return nil, err
	}

	if s.VertexFunc.Block == nil {
		return nil, fmt.Errorf("ebiten: vertex shader entry point '%s' is missing", vert)
	}
	if s.FragmentFunc.Block == nil {
		return nil, fmt.Errorf("ebiten: fragment shader entry point '%s' is missing", frag)
	}

	return &Shader{
		shader:       mipmap.NewShader(s),
		uniformNames: s.UniformNames,
		uniformTypes: s.Uniforms,
	}, nil
}

// Dispose disposes the shader program.
// After disposing, the shader is no longer available.
func (s *Shader) Dispose() {
	s.shader.MarkDisposed()
	s.shader = nil
}

func (s *Shader) convertUniforms(uniforms map[string]interface{}) []interface{} {
	type index struct {
		resultIndex        int
		shaderUniformIndex int
	}

	names := map[string]index{}
	var idx int
	for i, n := range s.uniformNames {
		if strings.HasPrefix(n, "__") {
			continue
		}
		names[n] = index{
			resultIndex:        idx,
			shaderUniformIndex: i,
		}
		idx++
	}

	us := make([]interface{}, len(names))
	for name, idx := range names {
		if v, ok := uniforms[name]; ok {
			// TODO: Check the uniform variable types?
			us[idx.resultIndex] = v
			continue
		}

		t := s.uniformTypes[idx.shaderUniformIndex]
		v := zeroUniformValue(t)
		if v == nil {
			panic(fmt.Sprintf("ebiten: unexpected uniform variable type: %s", t.String()))
		}
		us[idx.resultIndex] = v
	}

	// TODO: Panic if uniforms include an invalid name

	return us
}

func zeroUniformValue(t shaderir.Type) interface{} {
	switch t.Main {
	case shaderir.Bool:
		return false
	case shaderir.Int:
		return 0
	case shaderir.Float:
		return float32(0)
	case shaderir.Array:
		switch t.Sub[0].Main {
		case shaderir.Bool:
			return make([]bool, t.Length)
		case shaderir.Int:
			return make([]int, t.Length)
		default:
			return make([]float32, t.FloatNum())
		}
	default:
		return make([]float32, t.FloatNum())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/cespare/shadow/internal/github.com/BurntSushi/toml"
	"github.com/cespare/shadow/internal/github.com/cespare/hutil/apachelog"
)

var (
	// Dial with 5 second timeout (including name resolution).
	dial = func(network, addr string) (net.Conn, error) {
		return net.DialTimeout(network, addr, 5*time.Second)
	}
	client   *http.Client
	conf     *Conf
	confFile = flag.String("conf", "conf.toml", "Which config file to use")
)

func init() {
	flag.Parse()
	conf = &Conf{}
	_, err := toml.DecodeFile(*confFile, conf)
	if err != nil {
		log.Fatal(err)
	}
	if !(strings.HasPrefix(conf.GraphiteAddr, "http://") || strings.HasPrefix(conf.GraphiteAddr, "https://")) {
		conf.GraphiteAddr = "http://" + conf.GraphiteAddr
	}
	conf.GraphiteAddr = strings.TrimSuffix(conf.GraphiteAddr, "/")

	client = &http.Client{
		Transport: &http.Transport{
			Dial:                  dial,
			MaxIdleConnsPerHost:   10,
			ResponseHeaderTimeout: time.Duration(conf.GraphiteTimeoutSeconds) * time.Second,
		},
	}
}

func GraphiteURL(path string) string {
	return fmt.Sprintf("%s/%s", conf.GraphiteAddr, strings.TrimPrefix(path, "/"))
}

type Conf struct {
	ListenAddr             string `toml:"listen_addr"`
	GraphiteAddr           string `toml:"graphite_addr"`
	GraphiteTimeoutSeconds int    `toml:"graphite_timeout_seconds"`
}

// A Status is an HTTP status and a reason if it's not http.StatusOK.
type Status struct {
	Code    int
	Message string
}

func (s *Status) SetFromResponse(resp *http.Response) {
	s.Code = resp.StatusCode
	if s.Code == http.StatusOK {
		s.Message = ""
		return
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		s.Message = fmt.Sprintf("Could not read server response: %s", err)
		return
	}
	s.Message = string(b)
}

func (s *Status) WriteHTTPResponse(w http.ResponseWriter) {
	w.WriteHeader(s.Code)
	w.Write([]byte(s.String()))
}

func (s *Status) String() string {
	if s.Code == http.StatusOK {
		return "OK"
	}
	return fmt.Sprintf("NOT OK (%d): %s", s.Code, s.Message)
}

// SelfHealthChecker is our own health. We're assumed to be OK if we're running and if the Graphite we depend
// on is running.
type SelfHealthChecker struct {
	sync.Mutex
	*Status
	Frequency time.Duration
}

func (c *SelfHealthChecker) do() {
	log.Println("Running graphite health check")
	resp, err := client.Get(GraphiteURL("/")) // TODO: Is there a better health check route?
	c.Lock()
	defer c.Unlock()
	if err != nil {
		log.Println("Error contacting graphite:", err)
		c.Code = http.StatusBadGateway
		c.Message = fmt.Sprint("Error contacting graphite server:", err)
		return
	}
	log.Println("Graphite OK")
	c.SetFromResponse(resp)
	resp.Body.Close()
}

func (c *SelfHealthChecker) Run() {
	ticker := time.NewTicker(c.Frequency)
	c.do()
	for _ = range ticker.C {
		c.do()
	}
}

func (c *SelfHealthChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c.Lock()
	c.WriteHTTPResponse(w)
	c.Unlock()
}

func main() {
	selfChecker := &SelfHealthChecker{
		Status: &Status{
			Code:    http.StatusNotFound,
			Message: "Health check hasn't been run against Graphite yet",
		},
		Frequency: 30 * time.Second,
	}
	go selfChecker.Run()

	mux := http.NewServeMux()
	mux.Handle("/healthz", selfChecker)
	mux.HandleFunc("/check", HandleGraphiteChecks)
	mux.Handle("/", http.FileServer(http.Dir("static")))

	server := &http.Server{
		Addr:    conf.ListenAddr,
		Handler: apachelog.NewDefaultHandler(mux),
	}
	log.Println("Now listening on", conf.ListenAddr)
	log.Fatal(server.ListenAndServe())
}
<commit_msg>Add -version flag<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/cespare/shadow/internal/github.com/BurntSushi/toml"
	"github.com/cespare/shadow/internal/github.com/cespare/hutil/apachelog"
)

var (
	// Dial with 5 second timeout (including name resolution).
	dial = func(network, addr string) (net.Conn, error) {
		return net.DialTimeout(network, addr, 5*time.Second)
	}
	client   *http.Client
	conf     *Conf
	confFile = flag.String("conf", "conf.toml", "Which config file to use")

	version = "no version set" // overridable at build time with -ldflags -X
)

func GraphiteURL(path string) string {
	return fmt.Sprintf("%s/%s", conf.GraphiteAddr, strings.TrimPrefix(path, "/"))
}

type Conf struct {
	ListenAddr             string `toml:"listen_addr"`
	GraphiteAddr           string `toml:"graphite_addr"`
	GraphiteTimeoutSeconds int    `toml:"graphite_timeout_seconds"`
}

// A Status is an HTTP status and a reason if it's not http.StatusOK.
type Status struct {
	Code    int
	Message string
}

func (s *Status) SetFromResponse(resp *http.Response) {
	s.Code = resp.StatusCode
	if s.Code == http.StatusOK {
		s.Message = ""
		return
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		s.Message = fmt.Sprintf("Could not read server response: %s", err)
		return
	}
	s.Message = string(b)
}

func (s *Status) WriteHTTPResponse(w http.ResponseWriter) {
	w.WriteHeader(s.Code)
	w.Write([]byte(s.String()))
}

func (s *Status) String() string {
	if s.Code == http.StatusOK {
		return "OK"
	}
	return fmt.Sprintf("NOT OK (%d): %s", s.Code, s.Message)
}

// SelfHealthChecker is our own health. We're assumed to be OK if we're running and if the Graphite we depend
// on is running.
type SelfHealthChecker struct {
	sync.Mutex
	*Status
	Frequency time.Duration
}

func (c *SelfHealthChecker) do() {
	log.Println("Running graphite health check")
	resp, err := client.Get(GraphiteURL("/")) // TODO: Is there a better health check route?
	c.Lock()
	defer c.Unlock()
	if err != nil {
		log.Println("Error contacting graphite:", err)
		c.Code = http.StatusBadGateway
		c.Message = fmt.Sprint("Error contacting graphite server:", err)
		return
	}
	log.Println("Graphite OK")
	c.SetFromResponse(resp)
	resp.Body.Close()
}

func (c *SelfHealthChecker) Run() {
	ticker := time.NewTicker(c.Frequency)
	c.do()
	for _ = range ticker.C {
		c.do()
	}
}

func (c *SelfHealthChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c.Lock()
	c.WriteHTTPResponse(w)
	c.Unlock()
}

func main() {
	versionFlag := flag.Bool("version", false, "Display the version and exit")
	flag.Parse()

	if *versionFlag {
		fmt.Println(version)
		return
	}

	conf = &Conf{}
	_, err := toml.DecodeFile(*confFile, conf)
	if err != nil {
		log.Fatal(err)
	}
	if !(strings.HasPrefix(conf.GraphiteAddr, "http://") || strings.HasPrefix(conf.GraphiteAddr, "https://")) {
		conf.GraphiteAddr = "http://" + conf.GraphiteAddr
	}
	conf.GraphiteAddr = strings.TrimSuffix(conf.GraphiteAddr, "/")

	client = &http.Client{
		Transport: &http.Transport{
			Dial:                  dial,
			MaxIdleConnsPerHost:   10,
			ResponseHeaderTimeout: time.Duration(conf.GraphiteTimeoutSeconds) * time.Second,
		},
	}

	selfChecker := &SelfHealthChecker{
		Status: &Status{
			Code:    http.StatusNotFound,
			Message: "Health check hasn't been run against Graphite yet",
		},
		Frequency: 30 * time.Second,
	}
	go selfChecker.Run()

	mux := http.NewServeMux()
	mux.Handle("/healthz", selfChecker)
	mux.HandleFunc("/check", HandleGraphiteChecks)
	mux.Handle("/", http.FileServer(http.Dir("static")))

	server := &http.Server{
		Addr:    conf.ListenAddr,
		Handler: apachelog.NewDefaultHandler(mux),
	}
	log.Println("Now listening on", conf.ListenAddr)
	log.Fatal(server.ListenAndServe())
}
<|endoftext|>


<commit_before>package orm

import (
	"regexp"

	"gopkg.in/mgo.v2/bson"
)
import "github.com/lfq7413/tomato/utils"
import "strings"

var clpValidKeys = []string{"find", "get", "create", "update", "delete", "addField"}
var defaultClassLevelPermissions bson.M
var defaultColumns map[string]bson.M

func init() {
	defaultClassLevelPermissions = bson.M{}
	for _, v := range clpValidKeys {
		defaultClassLevelPermissions[v] = bson.M{
			"*": true,
		}
	}
	defaultColumns = map[string]bson.M{
		"_Default": bson.M{
			"objectId":  bson.M{"type": "String"},
			"createdAt": bson.M{"type": "Date"},
			"updatedAt": bson.M{"type": "Date"},
			"ACL":       bson.M{"type": "ACL"},
		},
		"_User": bson.M{
			"username":      bson.M{"type": "String"},
			"password":      bson.M{"type": "String"},
			"authData":      bson.M{"type": "Object"},
			"email":         bson.M{"type": "String"},
			"emailVerified": bson.M{"type": "Boolean"},
		},
		"_Installation": bson.M{
			"installationId":   bson.M{"type": "String"},
			"deviceToken":      bson.M{"type": "String"},
			"channels":         bson.M{"type": "Array"},
			"deviceType":       bson.M{"type": "String"},
			"pushType":         bson.M{"type": "String"},
			"GCMSenderId":      bson.M{"type": "String"},
			"timeZone":         bson.M{"type": "String"},
			"localeIdentifier": bson.M{"type": "String"},
			"badge":            bson.M{"type": "Number"},
		},
		"_Role": bson.M{
			"name":  bson.M{"type": "String"},
			"users": bson.M{"type": "Relation", "targetClass": "_User"},
			"roles": bson.M{"type": "Relation", "targetClass": "_Role"},
		},
		"_Session": bson.M{
			"restricted":     bson.M{"type": "Boolean"},
			"user":           bson.M{"type": "Pointer", "targetClass": "_User"},
			"installationId": bson.M{"type": "String"},
			"sessionToken":   bson.M{"type": "String"},
			"expiresAt":      bson.M{"type": "Date"},
			"createdWith":    bson.M{"type": "Object"},
		},
		"_Product": bson.M{
			"productIdentifier": bson.M{"type": "String"},
			"download":          bson.M{"type": "File"},
			"downloadName":      bson.M{"type": "String"},
			"icon":              bson.M{"type": "File"},
			"order":             bson.M{"type": "Number"},
			"title":             bson.M{"type": "String"},
			"subtitle":          bson.M{"type": "String"},
		},
	}
}

// Schema ...
type Schema struct {
	collection *MongoSchemaCollection
	data       bson.M
	perms      bson.M
}

// AddClassIfNotExists 添加类定义
func (s *Schema) AddClassIfNotExists(className string, fields bson.M, classLevelPermissions bson.M) bson.M {
	// TODO
	if s.data[className] != nil {
		// TODO 类已存在
		return nil
	}

	mongoObject := mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPermissions)
	if mongoObject["result"] == nil {
		// TODO 转换出现问题
		return nil
	}
	err := s.collection.addSchema(className, utils.MapInterface(mongoObject["result"]))
	if err != nil {
		// TODO 出现错误
		return nil
	}

	return utils.MapInterface(mongoObject["result"])
}

// UpdateClass 更新类
func (s *Schema) UpdateClass(className string, submittedFields bson.M, classLevelPermissions bson.M) bson.M {
	return nil
}

func (s *Schema) reloadData() {
	// TODO
	s.data = bson.M{}
	s.perms = bson.M{}
	results, err := s.collection.GetAllSchemas()
	if err != nil {
		return
	}
	for _, obj := range results {
		className := ""
		classData := bson.M{}
		var permsData interface{}

		for k, v := range obj {
			switch k {
			case "_id":
				className = utils.String(v)
			case "_metadata":
				if v != nil && utils.MapInterface(v) != nil && utils.MapInterface(v)["class_permissions"] != nil {
					permsData = utils.MapInterface(v)["class_permissions"]
				}
			default:
				classData[k] = v
			}
		}

		if className != "" {
			s.data[className] = classData
			if permsData != nil {
				s.perms[className] = permsData
			}
		}
	}
}

// MongoSchemaToSchemaAPIResponse ...
func MongoSchemaToSchemaAPIResponse(schema bson.M) bson.M {
	result := bson.M{
		"className": schema["_id"],
		"fields":    mongoSchemaAPIResponseFields(schema),
	}

	classLevelPermissions := utils.CopyMap(defaultClassLevelPermissions)
	if schema["_metadata"] != nil && utils.MapInterface(schema["_metadata"]) != nil {
		metadata := utils.MapInterface(schema["_metadata"])
		if metadata["class_permissions"] != nil && utils.MapInterface(metadata["class_permissions"]) != nil {
			classPermissions := utils.MapInterface(metadata["class_permissions"])
			for k, v := range classPermissions {
				classLevelPermissions[k] = v
			}
		}
	}
	result["classLevelPermissions"] = classLevelPermissions

	return result
}

var nonFieldSchemaKeys = []string{"_id", "_metadata", "_client_permissions"}

func mongoSchemaAPIResponseFields(schema bson.M) bson.M {
	fieldNames := []string{}
	for k := range schema {
		t := false
		for _, v := range nonFieldSchemaKeys {
			if k == v {
				t = true
				break
			}
		}
		if t == false {
			fieldNames = append(fieldNames, k)
		}
	}
	response := bson.M{}
	for _, v := range fieldNames {
		response[v] = mongoFieldTypeToSchemaAPIType(utils.String(schema[v]))
	}
	response["ACL"] = bson.M{
		"type": "ACL",
	}
	response["createdAt"] = bson.M{
		"type": "Date",
	}
	response["updatedAt"] = bson.M{
		"type": "Date",
	}
	response["objectId"] = bson.M{
		"type": "String",
	}
	return response
}

func mongoFieldTypeToSchemaAPIType(t string) bson.M {
	if t[0] == '*' {
		return bson.M{
			"type":        "Pointer",
			"targetClass": string(t[1:]),
		}
	}
	if strings.HasPrefix(t, "relation<") {
		return bson.M{
			"type":        "Relation",
			"targetClass": string(t[len("relation<") : len(t)-1]),
		}
	}
	switch t {
	case "number":
		return bson.M{
			"type": "Number",
		}
	case "string":
		return bson.M{
			"type": "String",
		}
	case "boolean":
		return bson.M{
			"type": "Boolean",
		}
	case "date":
		return bson.M{
			"type": "Date",
		}
	case "map":
		return bson.M{
			"type": "Object",
		}
	case "object":
		return bson.M{
			"type": "Object",
		}
	case "array":
		return bson.M{
			"type": "Array",
		}
	case "geopoint":
		return bson.M{
			"type": "GeoPoint",
		}
	case "file":
		return bson.M{
			"type": "File",
		}
	}

	return bson.M{}
}

func mongoSchemaFromFieldsAndClassNameAndCLP(fields bson.M, className string, classLevelPermissions bson.M) bson.M {
	if classNameIsValid(className) == false {
		// TODO 无效类名
		return nil
	}
	for fieldName := range fields {
		if fieldNameIsValid(fieldName) == false {
			// TODO 无效字段名
			return nil
		}
		if fieldNameIsValidForClass(fieldName, className) == false {
			// TODO 无法添加字段
			return nil
		}
	}

	mongoObject := bson.M{
		"_id":       className,
		"objectId":  "string",
		"updatedAt": "string",
		"createdAt": "string",
	}

	if defaultColumns[className] != nil {
		for fieldName := range defaultColumns[className] {
			validatedField := schemaAPITypeToMongoFieldType(utils.MapInterface(defaultColumns[className][fieldName]))
			if validatedField["result"] == nil {
				// TODO 转换错误
				return nil
			}
			mongoObject[fieldName] = validatedField["result"]
		}
	}

	for fieldName := range fields {
		validatedField := schemaAPITypeToMongoFieldType(utils.MapInterface(defaultColumns[className][fieldName]))
		if validatedField["result"] == nil {
			// TODO 转换错误
			return nil
		}
		mongoObject[fieldName] = validatedField["result"]
	}

	geoPoints := []string{}
	for k, v := range mongoObject {
		if utils.String(v) == "geopoint" {
			geoPoints = append(geoPoints, k)
		}
	}
	if len(geoPoints) > 1 {
		// TODO 只能有一个 geoPoint
		return nil
	}

	validateCLP(classLevelPermissions)
	var metadata bson.M
	if mongoObject["_metadata"] == nil && utils.MapInterface(mongoObject["_metadata"]) == nil {
		metadata = bson.M{}
	} else {
		metadata = utils.MapInterface(mongoObject["_metadata"])
	}
	if classLevelPermissions == nil {
		delete(metadata, "class_permissions")
	} else {
		metadata["class_permissions"] = classLevelPermissions
	}
	mongoObject["_metadata"] = metadata

	return bson.M{
		"result": mongoObject,
	}
}

func classNameIsValid(className string) bool {
	return className == "_User" ||
		className == "_Installation" ||
		className == "_Session" ||
		className == "_Role" ||
		className == "_Product" ||
		joinClassIsValid(className) ||
		fieldNameIsValid(className)
}

var joinClassRegex = `^_Join:[A-Za-z0-9_]+:[A-Za-z0-9_]+`

func joinClassIsValid(className string) bool {
	b, _ := regexp.MatchString(joinClassRegex, className)
	return b
}

var classAndFieldRegex = `^[A-Za-z][A-Za-z0-9_]*$`

func fieldNameIsValid(fieldName string) bool {
	b, _ := regexp.MatchString(classAndFieldRegex, fieldName)
	return b
}

func fieldNameIsValidForClass(fieldName string, className string) bool {
	if fieldNameIsValid(fieldName) == false {
		return false
	}
	if defaultColumns["_Default"][fieldName] != nil {
		return false
	}
	if defaultColumns[className] != nil && defaultColumns[className][fieldName] != nil {
		return false
	}

	return false
}

func schemaAPITypeToMongoFieldType(t bson.M) bson.M {
	if utils.String(t["type"]) == "" {
		// TODO type 无效
		return nil
	}
	apiType := utils.String(t["type"])

	if apiType == "Pointer" {
		if t["targetClass"] == nil {
			// TODO 需要 targetClass
			return nil
		}
		if utils.String(t["targetClass"]) == "" {
			// TODO targetClass 无效
			return nil
		}
		targetClass := utils.String(t["targetClass"])
		if classNameIsValid(targetClass) == false {
			// TODO 类名无效
			return nil
		}
		return bson.M{"result": "*" + targetClass}
	}
	if apiType == "Relation" {
		if t["targetClass"] == nil {
			// TODO 需要 targetClass
			return nil
		}
		if utils.String(t["targetClass"]) == "" {
			// TODO targetClass 无效
			return nil
		}
		targetClass := utils.String(t["targetClass"])
		if classNameIsValid(targetClass) == false {
			// TODO 类名无效
			return nil
		}
		return bson.M{"result": "relation<" + targetClass + ">"}
	}
	switch apiType {
	case "Number":
		return bson.M{"result": "number"}
	case "String":
		return bson.M{"result": "string"}
	case "Boolean":
		return bson.M{"result": "boolean"}
	case "Date":
		return bson.M{"result": "date"}
	case "Object":
		return bson.M{"result": "object"}
	case "Array":
		return bson.M{"result": "array"}
	case "GeoPoint":
		return bson.M{"result": "geopoint"}
	case "File":
		return bson.M{"result": "file"}
	default:
		// TODO type 不正确
		return nil
	}
}

func validateCLP(perms bson.M) {
	if perms == nil {
		return
	}

	for operation, perm := range perms {
		t := false
		for _, key := range clpValidKeys {
			if operation == key {
				t = true
				break
			}
		}
		if t == false {
			// TODO 不是有效操作
			return
		}

		for key, p := range utils.MapInterface(perm) {
			verifyPermissionKey(key)
			if v, ok := p.(bool); ok {
				if v == false {
					// TODO 值无效
					return
				}
			} else {
				// TODO 值无效
				return
			}
		}
	}
}

// 24 alpha numberic chars + uppercase
var userIDRegex = `^[a-zA-Z0-9]{24}$`

// Anything that start with role
var roleRegex = `^role:.*`

// * permission
var publicRegex = `^\*$`

var permissionKeyRegex = []string{userIDRegex, roleRegex, publicRegex}

func verifyPermissionKey(key string) {
	result := false
	for _, v := range permissionKeyRegex {
		b, _ := regexp.MatchString(v, key)
		result = result || b
	}
	if result == false {
		// TODO 无效的权限名称
		return
	}
}

// Load 返回一个新的 Schema 结构体
func Load(collection *MongoSchemaCollection) *Schema {
	schema := &Schema{
		collection: collection,
	}
	schema.reloadData()
	return schema
}
<commit_msg>完成 UpdateClass<commit_after>package orm

import (
	"regexp"

	"gopkg.in/mgo.v2/bson"
)
import "github.com/lfq7413/tomato/utils"
import "strings"

var clpValidKeys = []string{"find", "get", "create", "update", "delete", "addField"}
var defaultClassLevelPermissions bson.M
var defaultColumns map[string]bson.M

func init() {
	defaultClassLevelPermissions = bson.M{}
	for _, v := range clpValidKeys {
		defaultClassLevelPermissions[v] = bson.M{
			"*": true,
		}
	}
	defaultColumns = map[string]bson.M{
		"_Default": bson.M{
			"objectId":  bson.M{"type": "String"},
			"createdAt": bson.M{"type": "Date"},
			"updatedAt": bson.M{"type": "Date"},
			"ACL":       bson.M{"type": "ACL"},
		},
		"_User": bson.M{
			"username":      bson.M{"type": "String"},
			"password":      bson.M{"type": "String"},
			"authData":      bson.M{"type": "Object"},
			"email":         bson.M{"type": "String"},
			"emailVerified": bson.M{"type": "Boolean"},
		},
		"_Installation": bson.M{
			"installationId":   bson.M{"type": "String"},
			"deviceToken":      bson.M{"type": "String"},
			"channels":         bson.M{"type": "Array"},
			"deviceType":       bson.M{"type": "String"},
			"pushType":         bson.M{"type": "String"},
			"GCMSenderId":      bson.M{"type": "String"},
			"timeZone":         bson.M{"type": "String"},
			"localeIdentifier": bson.M{"type": "String"},
			"badge":            bson.M{"type": "Number"},
		},
		"_Role": bson.M{
			"name":  bson.M{"type": "String"},
			"users": bson.M{"type": "Relation", "targetClass": "_User"},
			"roles": bson.M{"type": "Relation", "targetClass": "_Role"},
		},
		"_Session": bson.M{
			"restricted":     bson.M{"type": "Boolean"},
			"user":           bson.M{"type": "Pointer", "targetClass": "_User"},
			"installationId": bson.M{"type": "String"},
			"sessionToken":   bson.M{"type": "String"},
			"expiresAt":      bson.M{"type": "Date"},
			"createdWith":    bson.M{"type": "Object"},
		},
		"_Product": bson.M{
			"productIdentifier": bson.M{"type": "String"},
			"download":          bson.M{"type": "File"},
			"downloadName":      bson.M{"type": "String"},
			"icon":              bson.M{"type": "File"},
			"order":             bson.M{"type": "Number"},
			"title":             bson.M{"type": "String"},
			"subtitle":          bson.M{"type": "String"},
		},
	}
}

// Schema ...
type Schema struct {
	collection *MongoSchemaCollection
	data       bson.M
	perms      bson.M
}

// AddClassIfNotExists 添加类定义
func (s *Schema) AddClassIfNotExists(className string, fields bson.M, classLevelPermissions bson.M) bson.M {
	if s.data[className] != nil {
		// TODO 类已存在
		return nil
	}

	mongoObject := mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPermissions)
	if mongoObject["result"] == nil {
		// TODO 转换出现问题
		return nil
	}
	err := s.collection.addSchema(className, utils.MapInterface(mongoObject["result"]))
	if err != nil {
		// TODO 出现错误
		return nil
	}

	return utils.MapInterface(mongoObject["result"])
}

// UpdateClass 更新类
func (s *Schema) UpdateClass(className string, submittedFields bson.M, classLevelPermissions bson.M) bson.M {
	if s.data[className] == nil {
		// TODO 类不存在
		return nil
	}
	existingFields := utils.CopyMap(utils.MapInterface(s.data[className]))
	existingFields["_id"] = className

	for name, v := range submittedFields {
		field := utils.MapInterface(v)
		op := utils.String(field["__op"])
		if existingFields[name] != nil && op != "Delete" {
			// TODO 字段已存在，不能更新
			return nil
		}
		if existingFields[name] == nil && op == "Delete" {
			// TODO 字不存在，不能删除
			return nil
		}
	}

	newSchema := buildMergedSchemaObject(existingFields, submittedFields)
	mongoObject := mongoSchemaFromFieldsAndClassNameAndCLP(newSchema, className, classLevelPermissions)
	if mongoObject["result"] == nil {
		// TODO 生成错误
		return nil
	}

	insertedFields := []string{}
	for name, v := range submittedFields {
		field := utils.MapInterface(v)
		op := utils.String(field["__op"])
		if op == "Delete" {
			s.deleteField(name, className)
		} else {
			insertedFields = append(insertedFields, name)
		}
	}

	s.reloadData()

	mongoResult := utils.MapInterface(mongoObject["result"])
	for _, fieldName := range insertedFields {
		mongoType := utils.String(mongoResult[fieldName])
		s.validateField(className, fieldName, mongoType, false)
	}

	s.setPermissions(className, classLevelPermissions)

	return MongoSchemaToSchemaAPIResponse(mongoResult)
}

func (s *Schema) deleteField(fieldName string, className string) {

}

func (s *Schema) validateField(className, key, fieldtype string, freeze bool) {

}

func (s *Schema) setPermissions(className string, perms bson.M) {

}

func (s *Schema) reloadData() {
	// TODO
	s.data = bson.M{}
	s.perms = bson.M{}
	results, err := s.collection.GetAllSchemas()
	if err != nil {
		return
	}
	for _, obj := range results {
		className := ""
		classData := bson.M{}
		var permsData interface{}

		for k, v := range obj {
			switch k {
			case "_id":
				className = utils.String(v)
			case "_metadata":
				if v != nil && utils.MapInterface(v) != nil && utils.MapInterface(v)["class_permissions"] != nil {
					permsData = utils.MapInterface(v)["class_permissions"]
				}
			default:
				classData[k] = v
			}
		}

		if className != "" {
			s.data[className] = classData
			if permsData != nil {
				s.perms[className] = permsData
			}
		}
	}
}

// MongoSchemaToSchemaAPIResponse ...
func MongoSchemaToSchemaAPIResponse(schema bson.M) bson.M {
	result := bson.M{
		"className": schema["_id"],
		"fields":    mongoSchemaAPIResponseFields(schema),
	}

	classLevelPermissions := utils.CopyMap(defaultClassLevelPermissions)
	if schema["_metadata"] != nil && utils.MapInterface(schema["_metadata"]) != nil {
		metadata := utils.MapInterface(schema["_metadata"])
		if metadata["class_permissions"] != nil && utils.MapInterface(metadata["class_permissions"]) != nil {
			classPermissions := utils.MapInterface(metadata["class_permissions"])
			for k, v := range classPermissions {
				classLevelPermissions[k] = v
			}
		}
	}
	result["classLevelPermissions"] = classLevelPermissions

	return result
}

var nonFieldSchemaKeys = []string{"_id", "_metadata", "_client_permissions"}

func mongoSchemaAPIResponseFields(schema bson.M) bson.M {
	fieldNames := []string{}
	for k := range schema {
		t := false
		for _, v := range nonFieldSchemaKeys {
			if k == v {
				t = true
				break
			}
		}
		if t == false {
			fieldNames = append(fieldNames, k)
		}
	}
	response := bson.M{}
	for _, v := range fieldNames {
		response[v] = mongoFieldTypeToSchemaAPIType(utils.String(schema[v]))
	}
	response["ACL"] = bson.M{
		"type": "ACL",
	}
	response["createdAt"] = bson.M{
		"type": "Date",
	}
	response["updatedAt"] = bson.M{
		"type": "Date",
	}
	response["objectId"] = bson.M{
		"type": "String",
	}
	return response
}

func mongoFieldTypeToSchemaAPIType(t string) bson.M {
	if t[0] == '*' {
		return bson.M{
			"type":        "Pointer",
			"targetClass": string(t[1:]),
		}
	}
	if strings.HasPrefix(t, "relation<") {
		return bson.M{
			"type":        "Relation",
			"targetClass": string(t[len("relation<") : len(t)-1]),
		}
	}
	switch t {
	case "number":
		return bson.M{
			"type": "Number",
		}
	case "string":
		return bson.M{
			"type": "String",
		}
	case "boolean":
		return bson.M{
			"type": "Boolean",
		}
	case "date":
		return bson.M{
			"type": "Date",
		}
	case "map":
		return bson.M{
			"type": "Object",
		}
	case "object":
		return bson.M{
			"type": "Object",
		}
	case "array":
		return bson.M{
			"type": "Array",
		}
	case "geopoint":
		return bson.M{
			"type": "GeoPoint",
		}
	case "file":
		return bson.M{
			"type": "File",
		}
	}

	return bson.M{}
}

func mongoSchemaFromFieldsAndClassNameAndCLP(fields bson.M, className string, classLevelPermissions bson.M) bson.M {
	if classNameIsValid(className) == false {
		// TODO 无效类名
		return nil
	}
	for fieldName := range fields {
		if fieldNameIsValid(fieldName) == false {
			// TODO 无效字段名
			return nil
		}
		if fieldNameIsValidForClass(fieldName, className) == false {
			// TODO 无法添加字段
			return nil
		}
	}

	mongoObject := bson.M{
		"_id":       className,
		"objectId":  "string",
		"updatedAt": "string",
		"createdAt": "string",
	}

	if defaultColumns[className] != nil {
		for fieldName := range defaultColumns[className] {
			validatedField := schemaAPITypeToMongoFieldType(utils.MapInterface(defaultColumns[className][fieldName]))
			if validatedField["result"] == nil {
				// TODO 转换错误
				return nil
			}
			mongoObject[fieldName] = validatedField["result"]
		}
	}

	for fieldName := range fields {
		validatedField := schemaAPITypeToMongoFieldType(utils.MapInterface(defaultColumns[className][fieldName]))
		if validatedField["result"] == nil {
			// TODO 转换错误
			return nil
		}
		mongoObject[fieldName] = validatedField["result"]
	}

	geoPoints := []string{}
	for k, v := range mongoObject {
		if utils.String(v) == "geopoint" {
			geoPoints = append(geoPoints, k)
		}
	}
	if len(geoPoints) > 1 {
		// TODO 只能有一个 geoPoint
		return nil
	}

	validateCLP(classLevelPermissions)
	var metadata bson.M
	if mongoObject["_metadata"] == nil && utils.MapInterface(mongoObject["_metadata"]) == nil {
		metadata = bson.M{}
	} else {
		metadata = utils.MapInterface(mongoObject["_metadata"])
	}
	if classLevelPermissions == nil {
		delete(metadata, "class_permissions")
	} else {
		metadata["class_permissions"] = classLevelPermissions
	}
	mongoObject["_metadata"] = metadata

	return bson.M{
		"result": mongoObject,
	}
}

func classNameIsValid(className string) bool {
	return className == "_User" ||
		className == "_Installation" ||
		className == "_Session" ||
		className == "_Role" ||
		className == "_Product" ||
		joinClassIsValid(className) ||
		fieldNameIsValid(className)
}

var joinClassRegex = `^_Join:[A-Za-z0-9_]+:[A-Za-z0-9_]+`

func joinClassIsValid(className string) bool {
	b, _ := regexp.MatchString(joinClassRegex, className)
	return b
}

var classAndFieldRegex = `^[A-Za-z][A-Za-z0-9_]*$`

func fieldNameIsValid(fieldName string) bool {
	b, _ := regexp.MatchString(classAndFieldRegex, fieldName)
	return b
}

func fieldNameIsValidForClass(fieldName string, className string) bool {
	if fieldNameIsValid(fieldName) == false {
		return false
	}
	if defaultColumns["_Default"][fieldName] != nil {
		return false
	}
	if defaultColumns[className] != nil && defaultColumns[className][fieldName] != nil {
		return false
	}

	return false
}

func schemaAPITypeToMongoFieldType(t bson.M) bson.M {
	if utils.String(t["type"]) == "" {
		// TODO type 无效
		return nil
	}
	apiType := utils.String(t["type"])

	if apiType == "Pointer" {
		if t["targetClass"] == nil {
			// TODO 需要 targetClass
			return nil
		}
		if utils.String(t["targetClass"]) == "" {
			// TODO targetClass 无效
			return nil
		}
		targetClass := utils.String(t["targetClass"])
		if classNameIsValid(targetClass) == false {
			// TODO 类名无效
			return nil
		}
		return bson.M{"result": "*" + targetClass}
	}
	if apiType == "Relation" {
		if t["targetClass"] == nil {
			// TODO 需要 targetClass
			return nil
		}
		if utils.String(t["targetClass"]) == "" {
			// TODO targetClass 无效
			return nil
		}
		targetClass := utils.String(t["targetClass"])
		if classNameIsValid(targetClass) == false {
			// TODO 类名无效
			return nil
		}
		return bson.M{"result": "relation<" + targetClass + ">"}
	}
	switch apiType {
	case "Number":
		return bson.M{"result": "number"}
	case "String":
		return bson.M{"result": "string"}
	case "Boolean":
		return bson.M{"result": "boolean"}
	case "Date":
		return bson.M{"result": "date"}
	case "Object":
		return bson.M{"result": "object"}
	case "Array":
		return bson.M{"result": "array"}
	case "GeoPoint":
		return bson.M{"result": "geopoint"}
	case "File":
		return bson.M{"result": "file"}
	default:
		// TODO type 不正确
		return nil
	}
}

func validateCLP(perms bson.M) {
	if perms == nil {
		return
	}

	for operation, perm := range perms {
		t := false
		for _, key := range clpValidKeys {
			if operation == key {
				t = true
				break
			}
		}
		if t == false {
			// TODO 不是有效操作
			return
		}

		for key, p := range utils.MapInterface(perm) {
			verifyPermissionKey(key)
			if v, ok := p.(bool); ok {
				if v == false {
					// TODO 值无效
					return
				}
			} else {
				// TODO 值无效
				return
			}
		}
	}
}

// 24 alpha numberic chars + uppercase
var userIDRegex = `^[a-zA-Z0-9]{24}$`

// Anything that start with role
var roleRegex = `^role:.*`

// * permission
var publicRegex = `^\*$`

var permissionKeyRegex = []string{userIDRegex, roleRegex, publicRegex}

func verifyPermissionKey(key string) {
	result := false
	for _, v := range permissionKeyRegex {
		b, _ := regexp.MatchString(v, key)
		result = result || b
	}
	if result == false {
		// TODO 无效的权限名称
		return
	}
}

func buildMergedSchemaObject(existingFields bson.M, submittedFields bson.M) bson.M {
	return nil
}

// Load 返回一个新的 Schema 结构体
func Load(collection *MongoSchemaCollection) *Schema {
	schema := &Schema{
		collection: collection,
	}
	schema.reloadData()
	return schema
}
<|endoftext|>


<commit_before>package semanticAnalyser

import "github.com/carlcui/expressive/ast"
import "github.com/carlcui/expressive/symbolTable"
import "github.com/carlcui/expressive/typing"
import "github.com/carlcui/expressive/token"
import "github.com/carlcui/expressive/logger"
import "github.com/carlcui/expressive/signature"
import "fmt"

// SemanticAnalysisVisitor is the general semantic analyser using visitor pattern
type SemanticAnalysisVisitor struct {
	logger logger.Logger
}

// VisitEnterProgramNode creates program scope
func (visitor *SemanticAnalysisVisitor) VisitEnterProgramNode(node *ast.ProgramNode) {
	scope := symbolTable.CreateScope(nil)
	node.SetScope(scope)
}

// VisitLeaveProgramNode closes program scope
func (visitor *SemanticAnalysisVisitor) VisitLeaveProgramNode(node *ast.ProgramNode) {

}

// stmts

// VisitEnterVariableDeclarationNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterVariableDeclarationNode(node *ast.VariableDeclarationNode) {

}

// VisitLeaveVariableDeclarationNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveVariableDeclarationNode(node *ast.VariableDeclarationNode) {

	identifier := node.Identifier.(*ast.IdentifierNode)

	var resolvedTyping typing.Typing

	if node.DeclaredType == nil && node.Expr == nil {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.GetLocation(), "Missing variable type")
		return
	}

	if node.DeclaredType != nil {
		declaredTyping := node.DeclaredType.GetTyping()

		if node.Expr == nil {
			resolvedTyping = declaredTyping
		} else {
			exprTyping := node.Expr.GetTyping()

			if !exprTyping.Equals(declaredTyping) {
				visitor.log(node.GetLocation(),
					"variable declared as "+declaredTyping.String()+", "+
						"but expression evaluated to "+exprTyping.String())

				resolvedTyping = typing.ERROR_TYPE
			} else {
				resolvedTyping = exprTyping
			}
		}
	} else {
		resolvedTyping = node.Expr.GetTyping()
	}

	scope := node.GetLocalScope()

	if scope.VariableDeclared(identifier.Tok.Raw) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(identifier.GetLocation(), "variable \""+identifier.Tok.Raw+"\" has already been declared")
		return
	}

	binding := scope.CreateBinding(identifier.Tok.Raw, identifier.Tok.Locator, resolvedTyping)
	identifier.SetTyping(resolvedTyping)
	identifier.SetBinding(binding)

	if node.Tok.TokenType == token.CONST {
		binding.IsVariable = false
	}

	node.SetTyping(typing.VOID)
}

// VisitEnterAssignmentNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterAssignmentNode(node *ast.AssignmentNode) {
}

// VisitLeaveAssignmentNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveAssignmentNode(node *ast.AssignmentNode) {

	identifier := node.Identifier.(*ast.IdentifierNode)

	binding := identifier.GetBinding()

	// If the binding is nil, then error is handled inside VisitIdentifierNode. If we pass a
	// null instance (not nil) to signature matching, it will log another error, which will
	// be miss-leading.
	if binding == nil {
		node.SetTyping(typing.ERROR_TYPE)
		return
	}

	if !binding.IsVariable {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(identifier.GetLocation(), "variable cannot be re-assigned")
		return
	}

	declaredType := identifier.GetTyping()
	exprType := node.Expr.GetTyping()

	if !declaredType.Equals(exprType) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.Expr.GetLocation(), "variable declared as "+declaredType.String()+", "+
			" but got "+exprType.String())
		return
	}

	node.SetTyping(typing.VOID)
}

// VisitEnterPrintNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterPrintNode(node *ast.PrintNode) {

}

// VisitLeavePrintNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeavePrintNode(node *ast.PrintNode) {
	stringExprTyping := node.StringExpr.GetTyping()
	if !stringExprTyping.Equals(typing.STRING) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.GetLocation(), "requires string type, but got "+stringExprTyping.String())
		return
	}

	node.SetTyping(typing.VOID)
}

// exprs

// VisitEnterTernaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterTernaryOperatorNode(node *ast.TernaryOperatorNode) {

}

// VisitLeaveTernaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveTernaryOperatorNode(node *ast.TernaryOperatorNode) {
	typing1 := node.Expr1.GetTyping()
	typing2 := node.Expr2.GetTyping()
	typing3 := node.Expr3.GetTyping()

	operator := node.Operator

	if !signature.HasSignature(operator, typing1, typing2, typing3) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.TypeCheckError(node.GetLocation(), operator, typing1, typing2, typing3)
		return
	}

	resultTyping := signature.ResultTyping(operator, typing1, typing2, typing3)

	node.SetTyping(resultTyping)
}

// VisitEnterBinaryOepratorNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterBinaryOepratorNode(node *ast.BinaryOperatorNode) {

}

// VisitLeaveBinaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveBinaryOperatorNode(node *ast.BinaryOperatorNode) {
	lhsTyping := node.Lhs.GetTyping()
	rhsTyping := node.Rhs.GetTyping()

	operator := node.Operator

	if !signature.HasSignature(operator, lhsTyping, rhsTyping) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.TypeCheckError(node.GetLocation(), operator, lhsTyping, rhsTyping)
		return
	}

	resultTyping := signature.ResultTyping(operator, lhsTyping, rhsTyping)

	node.SetTyping(resultTyping)
}

// VisitEnterUnaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterUnaryOperatorNode(node *ast.UnaryOperatorNode) {

}

// VisitLeaveUnaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveUnaryOperatorNode(node *ast.UnaryOperatorNode) {
	paramTyping := node.Expr.GetTyping()

	operator := node.Operator

	if !signature.HasSignature(operator, paramTyping) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.TypeCheckError(node.GetLocation(), operator, paramTyping)
		return
	}

	resultTyping := signature.ResultTyping(operator, paramTyping)

	node.SetTyping(resultTyping)
}

// literal nodes

// VisitIntegerNode do something
func (visitor *SemanticAnalysisVisitor) VisitIntegerNode(node *ast.IntegerNode) {
	node.SetTyping(typing.INT)
}

// VisitFloatNode do something
func (visitor *SemanticAnalysisVisitor) VisitFloatNode(node *ast.FloatNode) {
	node.SetTyping(typing.FLOAT)
}

// VisitCharacterNode do something
func (visitor *SemanticAnalysisVisitor) VisitCharacterNode(node *ast.CharacterNode) {
	node.SetTyping(typing.CHAR)
}

// VisitStringNode do something
func (visitor *SemanticAnalysisVisitor) VisitStringNode(node *ast.StringNode) {
	node.SetTyping(typing.STRING)
}

// VisitIdentifierNode do something
func (visitor *SemanticAnalysisVisitor) VisitIdentifierNode(node *ast.IdentifierNode) {
	if node.IsBeingDeclared() {
		return
	}

	binding := node.FindVariableBinding()

	if binding == nil {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.GetLocation(), "variable \""+node.Tok.Raw+"\" used before declared")
		return
	}

	node.SetTyping(binding.GetTyping())
	node.SetBinding(binding)
}

// VisitBooleanNode do something
func (visitor *SemanticAnalysisVisitor) VisitBooleanNode(node *ast.BooleanNode) {
	node.SetTyping(typing.BOOL)
}

// VisitTypeLiteralNode do something
func (visitor *SemanticAnalysisVisitor) VisitTypeLiteralNode(node *ast.TypeLiteralNode) {
	switch node.Tok.TokenType {
	case token.INT_KEYWORD:
		node.SetTyping(typing.INT)
		break
	case token.FLOAT_KEYWORD:
		node.SetTyping(typing.FLOAT)
		break
	case token.CHAR_KEYWORD:
		node.SetTyping(typing.CHAR)
		break
	case token.BOOL_KEYWORD:
		node.SetTyping(typing.BOOL)
		break
	case token.STRING_KEYWORD:
		node.SetTyping(typing.STRING)
		break
	default:
		node.SetTyping(typing.NO_TYPE)
	}
}

// VisitErrorNode do something
func (visitor *SemanticAnalysisVisitor) VisitErrorNode(node *ast.ErrorNode) {
	node.SetTyping(typing.ERROR_TYPE)
}

func (visitor *SemanticAnalysisVisitor) TypeCheckError(location string, key interface{}, params ...typing.Typing) {
	err := fmt.Errorf("%v does not support operation on %v", key, params)
	visitor.log(location, err.Error())
}

func (visitor *SemanticAnalysisVisitor) log(location string, message string) {
	visitor.logger.Log(location, message)
}
<commit_msg>add visitor funcs<commit_after>package semanticAnalyser

import "github.com/carlcui/expressive/ast"
import "github.com/carlcui/expressive/symbolTable"
import "github.com/carlcui/expressive/typing"
import "github.com/carlcui/expressive/token"
import "github.com/carlcui/expressive/logger"
import "github.com/carlcui/expressive/signature"
import "fmt"

// SemanticAnalysisVisitor is the general semantic analyser using visitor pattern
type SemanticAnalysisVisitor struct {
	logger logger.Logger
}

// VisitEnterProgramNode creates program scope
func (visitor *SemanticAnalysisVisitor) VisitEnterProgramNode(node *ast.ProgramNode) {
	scope := symbolTable.CreateScope(nil)
	node.SetScope(scope)
}

// VisitLeaveProgramNode closes program scope
func (visitor *SemanticAnalysisVisitor) VisitLeaveProgramNode(node *ast.ProgramNode) {

}

func (visitor *SemanticAnalysisVisitor) VisitEnterBlockNode(node *ast.BlockNode) {

}

func (visitor *SemanticAnalysisVisitor) VisitLeaveBlockNode(node *ast.BlockNode) {

}

// stmts

// VisitEnterVariableDeclarationNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterVariableDeclarationNode(node *ast.VariableDeclarationNode) {

}

// VisitLeaveVariableDeclarationNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveVariableDeclarationNode(node *ast.VariableDeclarationNode) {

	identifier := node.Identifier.(*ast.IdentifierNode)

	var resolvedTyping typing.Typing

	if node.DeclaredType == nil && node.Expr == nil {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.GetLocation(), "Missing variable type")
		return
	}

	if node.DeclaredType != nil {
		declaredTyping := node.DeclaredType.GetTyping()

		if node.Expr == nil {
			resolvedTyping = declaredTyping
		} else {
			exprTyping := node.Expr.GetTyping()

			if !exprTyping.Equals(declaredTyping) {
				visitor.log(node.GetLocation(),
					"variable declared as "+declaredTyping.String()+", "+
						"but expression evaluated to "+exprTyping.String())

				resolvedTyping = typing.ERROR_TYPE
			} else {
				resolvedTyping = exprTyping
			}
		}
	} else {
		resolvedTyping = node.Expr.GetTyping()
	}

	scope := node.GetLocalScope()

	if scope.VariableDeclared(identifier.Tok.Raw) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(identifier.GetLocation(), "variable \""+identifier.Tok.Raw+"\" has already been declared")
		return
	}

	binding := scope.CreateBinding(identifier.Tok.Raw, identifier.Tok.Locator, resolvedTyping)
	identifier.SetTyping(resolvedTyping)
	identifier.SetBinding(binding)

	if node.Tok.TokenType == token.CONST {
		binding.IsVariable = false
	}

	node.SetTyping(typing.VOID)
}

// VisitEnterAssignmentNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterAssignmentNode(node *ast.AssignmentNode) {
}

// VisitLeaveAssignmentNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveAssignmentNode(node *ast.AssignmentNode) {

	identifier := node.Identifier.(*ast.IdentifierNode)

	binding := identifier.GetBinding()

	// If the binding is nil, then error is handled inside VisitIdentifierNode. If we pass a
	// null instance (not nil) to signature matching, it will log another error, which will
	// be miss-leading.
	if binding == nil {
		node.SetTyping(typing.ERROR_TYPE)
		return
	}

	if !binding.IsVariable {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(identifier.GetLocation(), "variable cannot be re-assigned")
		return
	}

	declaredType := identifier.GetTyping()
	exprType := node.Expr.GetTyping()

	if !declaredType.Equals(exprType) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.Expr.GetLocation(), "variable declared as "+declaredType.String()+", "+
			" but got "+exprType.String())
		return
	}

	node.SetTyping(typing.VOID)
}

// VisitEnterPrintNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterPrintNode(node *ast.PrintNode) {

}

// VisitLeavePrintNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeavePrintNode(node *ast.PrintNode) {
	stringExprTyping := node.StringExpr.GetTyping()
	if !stringExprTyping.Equals(typing.STRING) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.GetLocation(), "requires string type, but got "+stringExprTyping.String())
		return
	}

	node.SetTyping(typing.VOID)
}

func (visitor *SemanticAnalysisVisitor) VisitEnterIfStmtNode(node *ast.IfStmtNode) {

}

func (visitor *SemanticAnalysisVisitor) VisitLeaveIfStmtNode(node *ast.IfStmtNode) {

}

// exprs

// VisitEnterTernaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterTernaryOperatorNode(node *ast.TernaryOperatorNode) {

}

// VisitLeaveTernaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveTernaryOperatorNode(node *ast.TernaryOperatorNode) {
	typing1 := node.Expr1.GetTyping()
	typing2 := node.Expr2.GetTyping()
	typing3 := node.Expr3.GetTyping()

	operator := node.Operator

	if !signature.HasSignature(operator, typing1, typing2, typing3) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.TypeCheckError(node.GetLocation(), operator, typing1, typing2, typing3)
		return
	}

	resultTyping := signature.ResultTyping(operator, typing1, typing2, typing3)

	node.SetTyping(resultTyping)
}

// VisitEnterBinaryOepratorNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterBinaryOepratorNode(node *ast.BinaryOperatorNode) {

}

// VisitLeaveBinaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveBinaryOperatorNode(node *ast.BinaryOperatorNode) {
	lhsTyping := node.Lhs.GetTyping()
	rhsTyping := node.Rhs.GetTyping()

	operator := node.Operator

	if !signature.HasSignature(operator, lhsTyping, rhsTyping) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.TypeCheckError(node.GetLocation(), operator, lhsTyping, rhsTyping)
		return
	}

	resultTyping := signature.ResultTyping(operator, lhsTyping, rhsTyping)

	node.SetTyping(resultTyping)
}

// VisitEnterUnaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitEnterUnaryOperatorNode(node *ast.UnaryOperatorNode) {

}

// VisitLeaveUnaryOperatorNode do something
func (visitor *SemanticAnalysisVisitor) VisitLeaveUnaryOperatorNode(node *ast.UnaryOperatorNode) {
	paramTyping := node.Expr.GetTyping()

	operator := node.Operator

	if !signature.HasSignature(operator, paramTyping) {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.TypeCheckError(node.GetLocation(), operator, paramTyping)
		return
	}

	resultTyping := signature.ResultTyping(operator, paramTyping)

	node.SetTyping(resultTyping)
}

// literal nodes

// VisitIntegerNode do something
func (visitor *SemanticAnalysisVisitor) VisitIntegerNode(node *ast.IntegerNode) {
	node.SetTyping(typing.INT)
}

// VisitFloatNode do something
func (visitor *SemanticAnalysisVisitor) VisitFloatNode(node *ast.FloatNode) {
	node.SetTyping(typing.FLOAT)
}

// VisitCharacterNode do something
func (visitor *SemanticAnalysisVisitor) VisitCharacterNode(node *ast.CharacterNode) {
	node.SetTyping(typing.CHAR)
}

// VisitStringNode do something
func (visitor *SemanticAnalysisVisitor) VisitStringNode(node *ast.StringNode) {
	node.SetTyping(typing.STRING)
}

// VisitIdentifierNode do something
func (visitor *SemanticAnalysisVisitor) VisitIdentifierNode(node *ast.IdentifierNode) {
	if node.IsBeingDeclared() {
		return
	}

	binding := node.FindVariableBinding()

	if binding == nil {
		node.SetTyping(typing.ERROR_TYPE)
		visitor.log(node.GetLocation(), "variable \""+node.Tok.Raw+"\" used before declared")
		return
	}

	node.SetTyping(binding.GetTyping())
	node.SetBinding(binding)
}

// VisitBooleanNode do something
func (visitor *SemanticAnalysisVisitor) VisitBooleanNode(node *ast.BooleanNode) {
	node.SetTyping(typing.BOOL)
}

// VisitTypeLiteralNode do something
func (visitor *SemanticAnalysisVisitor) VisitTypeLiteralNode(node *ast.TypeLiteralNode) {
	switch node.Tok.TokenType {
	case token.INT_KEYWORD:
		node.SetTyping(typing.INT)
		break
	case token.FLOAT_KEYWORD:
		node.SetTyping(typing.FLOAT)
		break
	case token.CHAR_KEYWORD:
		node.SetTyping(typing.CHAR)
		break
	case token.BOOL_KEYWORD:
		node.SetTyping(typing.BOOL)
		break
	case token.STRING_KEYWORD:
		node.SetTyping(typing.STRING)
		break
	default:
		node.SetTyping(typing.NO_TYPE)
	}
}

// VisitErrorNode do something
func (visitor *SemanticAnalysisVisitor) VisitErrorNode(node *ast.ErrorNode) {
	node.SetTyping(typing.ERROR_TYPE)
}

func (visitor *SemanticAnalysisVisitor) TypeCheckError(location string, key interface{}, params ...typing.Typing) {
	err := fmt.Errorf("%v does not support operation on %v", key, params)
	visitor.log(location, err.Error())
}

func (visitor *SemanticAnalysisVisitor) log(location string, message string) {
	visitor.logger.Log(location, message)
}
<|endoftext|>


<commit_before>package capabilities

import (
	"net/http"
	"os"
	"strings"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"context"
	"google.golang.org/api/container/v1"
	"sync"
	"io/ioutil"
	"io"
	"encoding/json"
	"fmt"
	"github.com/sirupsen/logrus"
)

const (
	defaultCredentialEnv = "GOOGLE_APPLICATION_CREDENTIALS"
)

var mutex = &sync.Mutex{}

func NewGKECapabilitiesHandler() *gkeVersionHandler {
	return &gkeVersionHandler{}
}

type gkeVersionHandler struct {
}

type errorResponse struct {
	Error string `json:"error"`
}

type requestBody struct {
	Credentials string `json:"credentials"`
	Zone        string `json:"zone"`
	ProjectId   string `json:"projectId"`
}

func (g *gkeVersionHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	if req.Method != http.MethodPost {
		writer.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	writer.Header().Set("Content-Type", "application/json")

	raw, err := ioutil.ReadAll(req.Body)

	var body requestBody
	err = json.Unmarshal(raw, &body)

	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("cannot parse request body: "+err.Error()))
		return
	}

	credentials := body.Credentials
	projectId := body.ProjectId
	zone := body.Zone

	if projectId == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid projectId"))
		return
	}

	if zone == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid zone"))
		return
	}

	if credentials == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid credentials"))
		return
	}

	client, err := g.getServiceClient(context.Background(), credentials)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	result, err := client.Projects.Zones.GetServerconfig(projectId, zone).Do()

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	serialized, err := json.Marshal(result)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	writer.Write(serialized)
}

func (g *gkeVersionHandler) handleErr(writer http.ResponseWriter, originalErr error) {
	resp := errorResponse{originalErr.Error()}

	asJson, err := json.Marshal(resp)

	if err != nil {
		logrus.Error("error while marshalling error message '" + originalErr.Error() + "' error was '" + err.Error() + "'")
		writer.Write([]byte(err.Error()))
		return
	}

	writer.Write([]byte(asJson))
}

func (g *gkeVersionHandler) getServiceClient(ctx context.Context, credentialContent string) (*container.Service, error) {
	// The google SDK has no sane way to pass in a TokenSource give all the different types (user, service account, etc)
	// So we actually set an environment variable and then unset it
	mutex.Lock()
	locked := true
	setEnv := false
	cleanup := func() {
		if setEnv {
			os.Unsetenv(defaultCredentialEnv)
		}

		if locked {
			mutex.Unlock()
			locked = false
		}
	}
	defer cleanup()

	file, err := ioutil.TempFile("", "credential-file")
	if err != nil {
		return nil, err
	}
	defer os.Remove(file.Name())
	defer file.Close()

	if _, err := io.Copy(file, strings.NewReader(credentialContent)); err != nil {
		return nil, err
	}

	setEnv = true
	os.Setenv(defaultCredentialEnv, file.Name())

	ts, err := google.DefaultTokenSource(ctx, container.CloudPlatformScope)
	if err != nil {
		return nil, err
	}

	// Unlocks
	cleanup()

	client := oauth2.NewClient(ctx, ts)
	service, err := container.New(client)

	if err != nil {
		return nil, err
	}
	return service, nil
}
<commit_msg>Fixing race condition by syncing to gke driver mutex in capabilities get code<commit_after>package capabilities

import (
	"net/http"
	"os"
	"strings"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"context"
	"google.golang.org/api/container/v1"
	"io/ioutil"
	"io"
	"encoding/json"
	"fmt"
	"github.com/sirupsen/logrus"
	"github.com/rancher/kontainer-engine/service"
	"sync"
)

const (
	defaultCredentialEnv = "GOOGLE_APPLICATION_CREDENTIALS"
)

var mutex = service.Drivers["gke"].(sync.Locker)

func NewGKECapabilitiesHandler() *gkeVersionHandler {
	return &gkeVersionHandler{}
}

type gkeVersionHandler struct {
}

type errorResponse struct {
	Error string `json:"error"`
}

type requestBody struct {
	Credentials string `json:"credentials"`
	Zone        string `json:"zone"`
	ProjectId   string `json:"projectId"`
}

func (g *gkeVersionHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	if req.Method != http.MethodPost {
		writer.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	writer.Header().Set("Content-Type", "application/json")

	raw, err := ioutil.ReadAll(req.Body)

	var body requestBody
	err = json.Unmarshal(raw, &body)

	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("cannot parse request body: "+err.Error()))
		return
	}

	credentials := body.Credentials
	projectId := body.ProjectId
	zone := body.Zone

	if projectId == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid projectId"))
		return
	}

	if zone == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid zone"))
		return
	}

	if credentials == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid credentials"))
		return
	}

	client, err := g.getServiceClient(context.Background(), credentials)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	result, err := client.Projects.Zones.GetServerconfig(projectId, zone).Do()

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	serialized, err := json.Marshal(result)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	writer.Write(serialized)
}

func (g *gkeVersionHandler) handleErr(writer http.ResponseWriter, originalErr error) {
	resp := errorResponse{originalErr.Error()}

	asJson, err := json.Marshal(resp)

	if err != nil {
		logrus.Error("error while marshalling error message '" + originalErr.Error() + "' error was '" + err.Error() + "'")
		writer.Write([]byte(err.Error()))
		return
	}

	writer.Write([]byte(asJson))
}

func (g *gkeVersionHandler) getServiceClient(ctx context.Context, credentialContent string) (*container.Service, error) {
	// The google SDK has no sane way to pass in a TokenSource give all the different types (user, service account, etc)
	// So we actually set an environment variable and then unset it
	mutex.Lock()
	locked := true
	setEnv := false
	cleanup := func() {
		if setEnv {
			os.Unsetenv(defaultCredentialEnv)
		}

		if locked {
			mutex.Unlock()
			locked = false
		}
	}
	defer cleanup()

	file, err := ioutil.TempFile("", "credential-file")
	if err != nil {
		return nil, err
	}
	defer os.Remove(file.Name())
	defer file.Close()

	if _, err := io.Copy(file, strings.NewReader(credentialContent)); err != nil {
		return nil, err
	}

	setEnv = true
	os.Setenv(defaultCredentialEnv, file.Name())

	ts, err := google.DefaultTokenSource(ctx, container.CloudPlatformScope)
	if err != nil {
		return nil, err
	}

	// Unlocks
	cleanup()

	client := oauth2.NewClient(ctx, ts)
	service, err := container.New(client)

	if err != nil {
		return nil, err
	}
	return service, nil
}
<|endoftext|>