<commit_before>package server

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Meh, we need to get rid of this shit

import (
	"context"
	"errors"
	"io"
	"reflect"
	"strings"
	"sync"
	"unicode"
	"unicode/utf8"

	"github.com/micro/go-micro/codec"
	"github.com/micro/go-micro/util/log"
)

var (
	lastStreamResponseError = errors.New("EOS")
	// A value sent as a placeholder for the server's response value when the server
	// receives an invalid request. It is never decoded by the client since the Response
	// contains an error when it is used.
	invalidRequest = struct{}{}

	// Precompute the reflect type for error. Can't use error directly
	// because Typeof takes an empty interface value. This is annoying.
	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
)

type methodType struct {
	sync.Mutex  // protects counters
	method      reflect.Method
	ArgType     reflect.Type
	ReplyType   reflect.Type
	ContextType reflect.Type
	stream      bool
}

type service struct {
	name   string                 // name of service
	rcvr   reflect.Value          // receiver of methods for the service
	typ    reflect.Type           // type of the receiver
	method map[string]*methodType // registered methods
}

type request struct {
	msg  *codec.Message
	next *request // for free list in Server
}

type response struct {
	msg  *codec.Message
	next *response // for free list in Server
}

// router represents an RPC router.
type router struct {
	name         string
	mu           sync.Mutex // protects the serviceMap
	serviceMap   map[string]*service
	reqLock      sync.Mutex // protects freeReq
	freeReq      *request
	respLock     sync.Mutex // protects freeResp
	freeResp     *response
	hdlrWrappers []HandlerWrapper
}

func newRpcRouter() *router {
	return &router{
		serviceMap: make(map[string]*service),
	}
}

// Is this an exported - upper case - name?
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}

// prepareMethod returns a methodType for the provided method or nil
// in case if the method was unsuitable.
func prepareMethod(method reflect.Method) *methodType {
	mtype := method.Type
	mname := method.Name
	var replyType, argType, contextType reflect.Type
	var stream bool

	// Method must be exported.
	if method.PkgPath != "" {
		return nil
	}

	switch mtype.NumIn() {
	case 3:
		// assuming streaming
		argType = mtype.In(2)
		contextType = mtype.In(1)
		stream = true
	case 4:
		// method that takes a context
		argType = mtype.In(2)
		replyType = mtype.In(3)
		contextType = mtype.In(1)
	default:
		log.Log("method", mname, "of", mtype, "has wrong number of ins:", mtype.NumIn())
		return nil
	}

	if stream {
		// check stream type
		streamType := reflect.TypeOf((*Stream)(nil)).Elem()
		if !argType.Implements(streamType) {
			log.Log(mname, "argument does not implement Stream interface:", argType)
			return nil
		}
	} else {
		// if not stream check the replyType

		// First arg need not be a pointer.
		if !isExportedOrBuiltinType(argType) {
			log.Log(mname, "argument type not exported:", argType)
			return nil
		}

		if replyType.Kind() != reflect.Ptr {
			log.Log("method", mname, "reply type not a pointer:", replyType)
			return nil
		}

		// Reply type must be exported.
		if !isExportedOrBuiltinType(replyType) {
			log.Log("method", mname, "reply type not exported:", replyType)
			return nil
		}
	}

	// Method needs one out.
	if mtype.NumOut() != 1 {
		log.Log("method", mname, "has wrong number of outs:", mtype.NumOut())
		return nil
	}
	// The return type of the method must be error.
	if returnType := mtype.Out(0); returnType != typeOfError {
		log.Log("method", mname, "returns", returnType.String(), "not error")
		return nil
	}
	return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream}
}

func (router *router) sendResponse(sending sync.Locker, req *request, reply interface{}, cc codec.Writer, last bool) error {
	msg := new(codec.Message)
	msg.Type = codec.Response
	resp := router.getResponse()
	resp.msg = msg

	resp.msg.Id = req.msg.Id
	sending.Lock()
	err := cc.Write(resp.msg, reply)
	sending.Unlock()
	router.freeResponse(resp)
	return err
}

func (s *service) call(ctx context.Context, router *router, sending *sync.Mutex, mtype *methodType, req *request, argv, replyv reflect.Value, cc codec.Writer) error {
	defer router.freeRequest(req)

	function := mtype.method.Func
	var returnValues []reflect.Value

	r := &rpcRequest{
		service:     req.msg.Target,
		contentType: req.msg.Header["Content-Type"],
		method:      req.msg.Method,
		endpoint:    req.msg.Endpoint,
		body:        req.msg.Body,
		rawBody:     argv.Interface(),
	}

	if !mtype.stream {
		fn := func(ctx context.Context, req Request, rsp interface{}) error {
			returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)})

			// The return value for the method is an error.
			if err := returnValues[0].Interface(); err != nil {
				return err.(error)
			}

			return nil
		}

		// wrap the handler
		for i := len(router.hdlrWrappers); i > 0; i-- {
			fn = router.hdlrWrappers[i-1](fn)
		}

		// execute handler
		if err := fn(ctx, r, replyv.Interface()); err != nil {
			return err
		}

		// send response
		return router.sendResponse(sending, req, replyv.Interface(), cc, true)
	}

	// declare a local error to see if we errored out already
	// keep track of the type, to make sure we return
	// the same one consistently
	rawStream := &rpcStream{
		context: ctx,
		codec:   cc.(codec.Codec),
		request: r,
		id:      req.msg.Id,
	}

	// Invoke the method, providing a new value for the reply.
	fn := func(ctx context.Context, req Request, stream interface{}) error {
		returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(stream)})
		if err := returnValues[0].Interface(); err != nil {
			// the function returned an error, we use that
			return err.(error)
		} else if serr := rawStream.Error(); serr == io.EOF || serr == io.ErrUnexpectedEOF {
			return nil
		} else {
			// no error, we send the special EOS error
			return lastStreamResponseError
		}
	}

	// wrap the handler
	for i := len(router.hdlrWrappers); i > 0; i-- {
		fn = router.hdlrWrappers[i-1](fn)
	}

	// client.Stream request
	r.stream = true

	// execute handler
	return fn(ctx, r, rawStream)
}

func (m *methodType) prepareContext(ctx context.Context) reflect.Value {
	if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
		return contextv
	}
	return reflect.Zero(m.ContextType)
}

func (router *router) getRequest() *request {
	router.reqLock.Lock()
	req := router.freeReq
	if req == nil {
		req = new(request)
	} else {
		router.freeReq = req.next
		*req = request{}
	}
	router.reqLock.Unlock()
	return req
}

func (router *router) freeRequest(req *request) {
	router.reqLock.Lock()
	req.next = router.freeReq
	router.freeReq = req
	router.reqLock.Unlock()
}

func (router *router) getResponse() *response {
	router.respLock.Lock()
	resp := router.freeResp
	if resp == nil {
		resp = new(response)
	} else {
		router.freeResp = resp.next
		*resp = response{}
	}
	router.respLock.Unlock()
	return resp
}

func (router *router) freeResponse(resp *response) {
	router.respLock.Lock()
	resp.next = router.freeResp
	router.freeResp = resp
	router.respLock.Unlock()
}

func (router *router) readRequest(r Request) (service *service, mtype *methodType, req *request, argv, replyv reflect.Value, keepReading bool, err error) {
	cc := r.Codec()

	service, mtype, req, keepReading, err = router.readHeader(cc)
	if err != nil {
		if !keepReading {
			return
		}
		// discard body
		cc.ReadBody(nil)
		return
	}
	// is it a streaming request? then we don't read the body
	if mtype.stream {
		cc.ReadBody(nil)
		return
	}

	// Decode the argument value.
	argIsValue := false // if true, need to indirect before calling.
	if mtype.ArgType.Kind() == reflect.Ptr {
		argv = reflect.New(mtype.ArgType.Elem())
	} else {
		argv = reflect.New(mtype.ArgType)
		argIsValue = true
	}
	// argv guaranteed to be a pointer now.
	if err = cc.ReadBody(argv.Interface()); err != nil {
		return
	}
	if argIsValue {
		argv = argv.Elem()
	}

	if !mtype.stream {
		replyv = reflect.New(mtype.ReplyType.Elem())
	}
	return
}

func (router *router) readHeader(cc codec.Reader) (service *service, mtype *methodType, req *request, keepReading bool, err error) {
	// Grab the request header.
	msg := new(codec.Message)
	msg.Type = codec.Request
	req = router.getRequest()
	req.msg = msg

	err = cc.ReadHeader(msg, msg.Type)
	if err != nil {
		req = nil
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return
		}
		err = errors.New("rpc: router cannot decode request: " + err.Error())
		return
	}

	// We read the header successfully. If we see an error now,
	// we can still recover and move on to the next request.
	keepReading = true

	serviceMethod := strings.Split(req.msg.Endpoint, ".")
	if len(serviceMethod) != 2 {
		err = errors.New("rpc: service/endpoint request ill-formed: " + req.msg.Endpoint)
		return
	}
	// Look up the request.
	router.mu.Lock()
	service = router.serviceMap[serviceMethod[0]]
	router.mu.Unlock()
	if service == nil {
		err = errors.New("rpc: can't find service " + serviceMethod[0])
		return
	}
	mtype = service.method[serviceMethod[1]]
	if mtype == nil {
		err = errors.New("rpc: can't find method " + serviceMethod[1])
	}
	return
}

func (router *router) NewHandler(h interface{}, opts ...HandlerOption) Handler {
	return newRpcHandler(h, opts...)
}

func (router *router) Handle(h Handler) error {
	router.mu.Lock()
	defer router.mu.Unlock()
	if router.serviceMap == nil {
		router.serviceMap = make(map[string]*service)
	}

	if len(h.Name()) == 0 {
		return errors.New("rpc.Handle: handler has no name")
	}
	if !isExported(h.Name()) {
		return errors.New("rpc.Handle: type " + h.Name() + " is not exported")
	}

	rcvr := h.Handler()
	s := new(service)
	s.typ = reflect.TypeOf(rcvr)
	s.rcvr = reflect.ValueOf(rcvr)

	// check name
	if _, present := router.serviceMap[h.Name()]; present {
		return errors.New("rpc.Handle: service already defined: " + h.Name())
	}

	s.name = h.Name()
	s.method = make(map[string]*methodType)

	// Install the methods
	for m := 0; m < s.typ.NumMethod(); m++ {
		method := s.typ.Method(m)
		if mt := prepareMethod(method); mt != nil {
			s.method[method.Name] = mt
		}
	}

	// Check there are methods
	if len(s.method) == 0 {
		return errors.New("rpc Register: type " + s.name + " has no exported methods of suitable type")
	}

	// save handler
	router.serviceMap[s.name] = s
	return nil
}

func (router *router) ServeRequest(ctx context.Context, r Request, rsp Response) error {
	sending := new(sync.Mutex)
	service, mtype, req, argv, replyv, keepReading, err := router.readRequest(r)
	if err != nil {
		if !keepReading {
			return err
		}
		// send a response if we actually managed to read a header.
		if req != nil {
			router.freeRequest(req)
		}
		return err
	}
	return service.call(ctx, router, sending, mtype, req, argv, replyv, rsp.Codec())
}
<commit_msg>Fix a streaming bug<commit_after>package server

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Meh, we need to get rid of this shit

import (
	"context"
	"errors"
	"io"
	"reflect"
	"strings"
	"sync"
	"unicode"
	"unicode/utf8"

	"github.com/micro/go-micro/codec"
	"github.com/micro/go-micro/util/log"
)

var (
	lastStreamResponseError = errors.New("EOS")
	// A value sent as a placeholder for the server's response value when the server
	// receives an invalid request. It is never decoded by the client since the Response
	// contains an error when it is used.
	invalidRequest = struct{}{}

	// Precompute the reflect type for error. Can't use error directly
	// because Typeof takes an empty interface value. This is annoying.
	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
)

type methodType struct {
	sync.Mutex  // protects counters
	method      reflect.Method
	ArgType     reflect.Type
	ReplyType   reflect.Type
	ContextType reflect.Type
	stream      bool
}

type service struct {
	name   string                 // name of service
	rcvr   reflect.Value          // receiver of methods for the service
	typ    reflect.Type           // type of the receiver
	method map[string]*methodType // registered methods
}

type request struct {
	msg  *codec.Message
	next *request // for free list in Server
}

type response struct {
	msg  *codec.Message
	next *response // for free list in Server
}

// router represents an RPC router.
type router struct {
	name         string
	mu           sync.Mutex // protects the serviceMap
	serviceMap   map[string]*service
	reqLock      sync.Mutex // protects freeReq
	freeReq      *request
	respLock     sync.Mutex // protects freeResp
	freeResp     *response
	hdlrWrappers []HandlerWrapper
}

func newRpcRouter() *router {
	return &router{
		serviceMap: make(map[string]*service),
	}
}

// Is this an exported - upper case - name?
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}

// prepareMethod returns a methodType for the provided method or nil
// in case if the method was unsuitable.
func prepareMethod(method reflect.Method) *methodType {
	mtype := method.Type
	mname := method.Name
	var replyType, argType, contextType reflect.Type
	var stream bool

	// Method must be exported.
	if method.PkgPath != "" {
		return nil
	}

	switch mtype.NumIn() {
	case 3:
		// assuming streaming
		argType = mtype.In(2)
		contextType = mtype.In(1)
		stream = true
	case 4:
		// method that takes a context
		argType = mtype.In(2)
		replyType = mtype.In(3)
		contextType = mtype.In(1)
	default:
		log.Log("method", mname, "of", mtype, "has wrong number of ins:", mtype.NumIn())
		return nil
	}

	if stream {
		// check stream type
		streamType := reflect.TypeOf((*Stream)(nil)).Elem()
		if !argType.Implements(streamType) {
			log.Log(mname, "argument does not implement Stream interface:", argType)
			return nil
		}
	} else {
		// if not stream check the replyType

		// First arg need not be a pointer.
		if !isExportedOrBuiltinType(argType) {
			log.Log(mname, "argument type not exported:", argType)
			return nil
		}

		if replyType.Kind() != reflect.Ptr {
			log.Log("method", mname, "reply type not a pointer:", replyType)
			return nil
		}

		// Reply type must be exported.
		if !isExportedOrBuiltinType(replyType) {
			log.Log("method", mname, "reply type not exported:", replyType)
			return nil
		}
	}

	// Method needs one out.
	if mtype.NumOut() != 1 {
		log.Log("method", mname, "has wrong number of outs:", mtype.NumOut())
		return nil
	}
	// The return type of the method must be error.
	if returnType := mtype.Out(0); returnType != typeOfError {
		log.Log("method", mname, "returns", returnType.String(), "not error")
		return nil
	}
	return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream}
}

func (router *router) sendResponse(sending sync.Locker, req *request, reply interface{}, cc codec.Writer, last bool) error {
	msg := new(codec.Message)
	msg.Type = codec.Response
	resp := router.getResponse()
	resp.msg = msg

	resp.msg.Id = req.msg.Id
	sending.Lock()
	err := cc.Write(resp.msg, reply)
	sending.Unlock()
	router.freeResponse(resp)
	return err
}

func (s *service) call(ctx context.Context, router *router, sending *sync.Mutex, mtype *methodType, req *request, argv, replyv reflect.Value, cc codec.Writer) error {
	defer router.freeRequest(req)

	function := mtype.method.Func
	var returnValues []reflect.Value

	r := &rpcRequest{
		service:     req.msg.Target,
		contentType: req.msg.Header["Content-Type"],
		method:      req.msg.Method,
		endpoint:    req.msg.Endpoint,
		body:        req.msg.Body,
	}

	// only set if not nil
	if argv.IsValid() {
		r.rawBody = argv.Interface()
	}

	if !mtype.stream {
		fn := func(ctx context.Context, req Request, rsp interface{}) error {
			returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)})

			// The return value for the method is an error.
			if err := returnValues[0].Interface(); err != nil {
				return err.(error)
			}

			return nil
		}

		// wrap the handler
		for i := len(router.hdlrWrappers); i > 0; i-- {
			fn = router.hdlrWrappers[i-1](fn)
		}

		// execute handler
		if err := fn(ctx, r, replyv.Interface()); err != nil {
			return err
		}

		// send response
		return router.sendResponse(sending, req, replyv.Interface(), cc, true)
	}

	// declare a local error to see if we errored out already
	// keep track of the type, to make sure we return
	// the same one consistently
	rawStream := &rpcStream{
		context: ctx,
		codec:   cc.(codec.Codec),
		request: r,
		id:      req.msg.Id,
	}

	// Invoke the method, providing a new value for the reply.
	fn := func(ctx context.Context, req Request, stream interface{}) error {
		returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(stream)})
		if err := returnValues[0].Interface(); err != nil {
			// the function returned an error, we use that
			return err.(error)
		} else if serr := rawStream.Error(); serr == io.EOF || serr == io.ErrUnexpectedEOF {
			return nil
		} else {
			// no error, we send the special EOS error
			return lastStreamResponseError
		}
	}

	// wrap the handler
	for i := len(router.hdlrWrappers); i > 0; i-- {
		fn = router.hdlrWrappers[i-1](fn)
	}

	// client.Stream request
	r.stream = true

	// execute handler
	return fn(ctx, r, rawStream)
}

func (m *methodType) prepareContext(ctx context.Context) reflect.Value {
	if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
		return contextv
	}
	return reflect.Zero(m.ContextType)
}

func (router *router) getRequest() *request {
	router.reqLock.Lock()
	req := router.freeReq
	if req == nil {
		req = new(request)
	} else {
		router.freeReq = req.next
		*req = request{}
	}
	router.reqLock.Unlock()
	return req
}

func (router *router) freeRequest(req *request) {
	router.reqLock.Lock()
	req.next = router.freeReq
	router.freeReq = req
	router.reqLock.Unlock()
}

func (router *router) getResponse() *response {
	router.respLock.Lock()
	resp := router.freeResp
	if resp == nil {
		resp = new(response)
	} else {
		router.freeResp = resp.next
		*resp = response{}
	}
	router.respLock.Unlock()
	return resp
}

func (router *router) freeResponse(resp *response) {
	router.respLock.Lock()
	resp.next = router.freeResp
	router.freeResp = resp
	router.respLock.Unlock()
}

func (router *router) readRequest(r Request) (service *service, mtype *methodType, req *request, argv, replyv reflect.Value, keepReading bool, err error) {
	cc := r.Codec()

	service, mtype, req, keepReading, err = router.readHeader(cc)
	if err != nil {
		if !keepReading {
			return
		}
		// discard body
		cc.ReadBody(nil)
		return
	}
	// is it a streaming request? then we don't read the body
	if mtype.stream {
		cc.ReadBody(nil)
		return
	}

	// Decode the argument value.
	argIsValue := false // if true, need to indirect before calling.
	if mtype.ArgType.Kind() == reflect.Ptr {
		argv = reflect.New(mtype.ArgType.Elem())
	} else {
		argv = reflect.New(mtype.ArgType)
		argIsValue = true
	}
	// argv guaranteed to be a pointer now.
	if err = cc.ReadBody(argv.Interface()); err != nil {
		return
	}
	if argIsValue {
		argv = argv.Elem()
	}

	if !mtype.stream {
		replyv = reflect.New(mtype.ReplyType.Elem())
	}
	return
}

func (router *router) readHeader(cc codec.Reader) (service *service, mtype *methodType, req *request, keepReading bool, err error) {
	// Grab the request header.
	msg := new(codec.Message)
	msg.Type = codec.Request
	req = router.getRequest()
	req.msg = msg

	err = cc.ReadHeader(msg, msg.Type)
	if err != nil {
		req = nil
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return
		}
		err = errors.New("rpc: router cannot decode request: " + err.Error())
		return
	}

	// We read the header successfully. If we see an error now,
	// we can still recover and move on to the next request.
	keepReading = true

	serviceMethod := strings.Split(req.msg.Endpoint, ".")
	if len(serviceMethod) != 2 {
		err = errors.New("rpc: service/endpoint request ill-formed: " + req.msg.Endpoint)
		return
	}
	// Look up the request.
	router.mu.Lock()
	service = router.serviceMap[serviceMethod[0]]
	router.mu.Unlock()
	if service == nil {
		err = errors.New("rpc: can't find service " + serviceMethod[0])
		return
	}
	mtype = service.method[serviceMethod[1]]
	if mtype == nil {
		err = errors.New("rpc: can't find method " + serviceMethod[1])
	}
	return
}

func (router *router) NewHandler(h interface{}, opts ...HandlerOption) Handler {
	return newRpcHandler(h, opts...)
}

func (router *router) Handle(h Handler) error {
	router.mu.Lock()
	defer router.mu.Unlock()
	if router.serviceMap == nil {
		router.serviceMap = make(map[string]*service)
	}

	if len(h.Name()) == 0 {
		return errors.New("rpc.Handle: handler has no name")
	}
	if !isExported(h.Name()) {
		return errors.New("rpc.Handle: type " + h.Name() + " is not exported")
	}

	rcvr := h.Handler()
	s := new(service)
	s.typ = reflect.TypeOf(rcvr)
	s.rcvr = reflect.ValueOf(rcvr)

	// check name
	if _, present := router.serviceMap[h.Name()]; present {
		return errors.New("rpc.Handle: service already defined: " + h.Name())
	}

	s.name = h.Name()
	s.method = make(map[string]*methodType)

	// Install the methods
	for m := 0; m < s.typ.NumMethod(); m++ {
		method := s.typ.Method(m)
		if mt := prepareMethod(method); mt != nil {
			s.method[method.Name] = mt
		}
	}

	// Check there are methods
	if len(s.method) == 0 {
		return errors.New("rpc Register: type " + s.name + " has no exported methods of suitable type")
	}

	// save handler
	router.serviceMap[s.name] = s
	return nil
}

func (router *router) ServeRequest(ctx context.Context, r Request, rsp Response) error {
	sending := new(sync.Mutex)
	service, mtype, req, argv, replyv, keepReading, err := router.readRequest(r)
	if err != nil {
		if !keepReading {
			return err
		}
		// send a response if we actually managed to read a header.
		if req != nil {
			router.freeRequest(req)
		}
		return err
	}
	return service.call(ctx, router, sending, mtype, req, argv, replyv, rsp.Codec())
}
<|endoftext|>


<commit_before>package algoliasearch

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

type ABTest struct {
	Name     string
	Variants []Variant
	EndAt    time.Time
}

const ISO8601 = "2006-01-02T15:04:05Z"

func (abTest ABTest) MarshalJSON() ([]byte, error) {
	jsonVariants, err := json.Marshal(abTest.Variants)
	if err != nil {
		return nil, fmt.Errorf("cannot marshal variants: %s", err)
	}
	jsonAbTest := fmt.Sprintf(
		`{"name":"%s","variants":%s,"endAt":"%s"}`,
		abTest.Name,
		jsonVariants,
		abTest.EndAt.In(time.UTC).Format(ISO8601),
	)
	return []byte(jsonAbTest), nil
}

type Variant struct {
	Index             string `json:"index"`
	TrafficPercentage int    `json:"trafficPercentage"`
	Description       string `json:"description,omitempty"`
}

type ABTestTaskRes struct {
	ABTestID int    `json:"abTestID"`
	Index    string `json:"index"`
	TaskID   int    `json:"taskID"`
}

type GetABTestsRes struct {
	ABTests []ABTestResponse `json:"abtests"`
	Count   int              `json:"count"`
	Total   int              `json:"total"`
}

type ABTestResponse struct {
	ABTestID               int               `json:"abTestID"`
	ClickSignificance      int               `json:"clickSignificance"`
	ConversionSignificance float64           `json:"conversionSignificance"`
	CreatedAt              time.Time         `json:"createdAt"`
	EndAt                  time.Time         `json:"endAt"`
	Name                   string            `json:"name"`
	Status                 string            `json:"status"`
	Variants               []VariantResponse `json:"variants"`
}

type VariantResponse struct {
	AverageClickPosition int     `json:"averageClickPosition"`
	ClickCount           int     `json:"clickCount"`
	ClickThroughRate     float64 `json:"clickThroughRate"`
	ConversionCount      int     `json:"conversionCount"`
	ConversionRate       float64 `json:"conversionRate"`
	Description          string  `json:"description"`
	Index                string  `json:"index"`
	NoResultCount        int     `json:"noResultCount"`
	SearchCount          int     `json:"searchCount"`
	TrafficPercentage    int     `json:"trafficPercentage"`
	UserCount            int     `json:"userCount"`
}
<commit_msg>added: ABTest variants now support customSearchParameters<commit_after>package algoliasearch

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

type ABTest struct {
	Name     string
	Variants []Variant
	EndAt    time.Time
}

const ISO8601 = "2006-01-02T15:04:05Z"

func (abTest ABTest) MarshalJSON() ([]byte, error) {
	jsonVariants, err := json.Marshal(abTest.Variants)
	if err != nil {
		return nil, fmt.Errorf("cannot marshal variants: %s", err)
	}
	jsonAbTest := fmt.Sprintf(
		`{"name":"%s","variants":%s,"endAt":"%s"}`,
		abTest.Name,
		jsonVariants,
		abTest.EndAt.In(time.UTC).Format(ISO8601),
	)
	return []byte(jsonAbTest), nil
}

type Variant struct {
	Index                  string `json:"index"`
	TrafficPercentage      int    `json:"trafficPercentage"`
	Description            string `json:"description,omitempty"`
	CustomSearchParameters Map    `json:"customSearchParameters,omitempty"`
}

type ABTestTaskRes struct {
	ABTestID int    `json:"abTestID"`
	Index    string `json:"index"`
	TaskID   int    `json:"taskID"`
}

type GetABTestsRes struct {
	ABTests []ABTestResponse `json:"abtests"`
	Count   int              `json:"count"`
	Total   int              `json:"total"`
}

type ABTestResponse struct {
	ABTestID               int               `json:"abTestID"`
	ClickSignificance      int               `json:"clickSignificance"`
	ConversionSignificance float64           `json:"conversionSignificance"`
	CreatedAt              time.Time         `json:"createdAt"`
	EndAt                  time.Time         `json:"endAt"`
	Name                   string            `json:"name"`
	Status                 string            `json:"status"`
	Variants               []VariantResponse `json:"variants"`
}

type VariantResponse struct {
	AverageClickPosition int     `json:"averageClickPosition"`
	ClickCount           int     `json:"clickCount"`
	ClickThroughRate     float64 `json:"clickThroughRate"`
	ConversionCount      int     `json:"conversionCount"`
	ConversionRate       float64 `json:"conversionRate"`
	Description          string  `json:"description"`
	Index                string  `json:"index"`
	NoResultCount        int     `json:"noResultCount"`
	SearchCount          int     `json:"searchCount"`
	TrafficPercentage    int     `json:"trafficPercentage"`
	UserCount            int     `json:"userCount"`
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/joho/godotenv"
	"github.com/kelseyhightower/envconfig"
	"github.com/pkg/errors"
)

// AWSScaler scales nodes back by Amazon web services
type AWSScaler struct {
	svc  *autoscaling.AutoScaling
	spec AWSSpec
}

// AWSSpec defaults the specification for aws node scaling
type AWSSpec struct {
	envFile                string `envconfig:"AWS_ENV_FILE"`
	managerConfigName      string `envconfig:"AWS_MANAGER_CONFIG_NAME"`
	workerConfigName       string `envconfig:"AWS_WORKER_CONFIG_NAME"`
	region                 string `envconfig:"AWS_DEFAULT_REGION"`
	defaultMinManagerNodes uint64 `envconfig:"DEFAULT_MIN_MANAGER_NODES"`
	defaultMaxManagerNodes uint64 `envconfig:"DEFAULT_MAX_MANAGER_NODES"`
	defaultMinWorkerNodes  uint64 `envconfig:"DEFAULT_MIN_WORKER_NODES"`
	defaultMaxWorkerNodes  uint64 `envconfig:"DEFAULT_MAX_WORKER_NODES"`
}

// NewAWSScalerFromEnv creates an AWS based node scaler
func NewAWSScalerFromEnv() (*AWSScaler, error) {

	var spec AWSSpec
	err := envconfig.Process("", &spec)
	if err != nil {
		return nil, errors.Wrap(err, "Unable to get process env vars")
	}

	godotenv.Load(spec.envFile)

	sess, err := session.NewSession()
	if err != nil {
		return nil, errors.Wrap(err, "Unable to create aws session")
	}
	svc := autoscaling.New(sess, aws.NewConfig().WithRegion(spec.region))
	return &AWSScaler{
		svc:  svc,
		spec: spec,
	}, nil
}

// ScaleWorkerByDelta scales aws worker nodes by delta
func (s *AWSScaler) ScaleWorkerByDelta(delta int) (uint64, uint64, error) {
	return s.scaleNodes(delta, s.spec.workerConfigName, int64(s.spec.defaultMinWorkerNodes),
		int64(s.spec.defaultMaxWorkerNodes))
}

// ScaleManagerByDelta scales aws manager nodes by delta
func (s *AWSScaler) ScaleManagerByDelta(delta int) (uint64, uint64, error) {
	return s.scaleNodes(delta, s.spec.managerConfigName, int64(s.spec.defaultMinManagerNodes),
		int64(s.spec.defaultMaxManagerNodes))
}

func (s *AWSScaler) scaleNodes(delta int, configName string, minSize int64, maxSize int64) (uint64, uint64, error) {

	groupsOutput, err := s.svc.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{})
	if err != nil {
		return 0, 0, errors.Wrap(err, "Unable to describe-auto-scaling-groups")
	}

	var targetGroup *autoscaling.Group
	for _, group := range groupsOutput.AutoScalingGroups {
		if *group.LaunchConfigurationName == configName {
			targetGroup = group
			break
		}
	}
	if targetGroup == nil {
		return 0, 0, fmt.Errorf("Unable to find launch-configuration-name: %s", configName)
	}

	currentCapacity := *targetGroup.DesiredCapacity
	newCapacity := currentCapacity + int64(delta)
	if newCapacity < 0 {
		newCapacity = 0
	}

	// Check if newCapacity is in bounds
	if newCapacity < minSize || newCapacity > maxSize {
		return 0, 0, fmt.Errorf("New capacity: %d is not in between %d and %d", newCapacity, minSize, maxSize)
	}

	_, err = s.svc.UpdateAutoScalingGroup(&autoscaling.UpdateAutoScalingGroupInput{DesiredCapacity: &newCapacity,
		MinSize: &minSize,
		MaxSize: &maxSize})
	if err != nil {
		return 0, 0, errors.Wrap(err,
			fmt.Sprintf("Error calling update-auto-scaling-group with desired-capacity: %d", newCapacity))
	}
	return uint64(currentCapacity), uint64(newCapacity), nil
}
<commit_msg>RFC: Remove AWS_ENV var from spec<commit_after>package service

import (
	"fmt"
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/joho/godotenv"
	"github.com/kelseyhightower/envconfig"
	"github.com/pkg/errors"
)

// AWSScaler scales nodes back by Amazon web services
type AWSScaler struct {
	svc  *autoscaling.AutoScaling
	spec AWSSpec
}

// AWSSpec defaults the specification for aws node scaling
type AWSSpec struct {
	managerConfigName      string `envconfig:"AWS_MANAGER_CONFIG_NAME"`
	workerConfigName       string `envconfig:"AWS_WORKER_CONFIG_NAME"`
	region                 string `envconfig:"AWS_DEFAULT_REGION"`
	defaultMinManagerNodes uint64 `envconfig:"DEFAULT_MIN_MANAGER_NODES"`
	defaultMaxManagerNodes uint64 `envconfig:"DEFAULT_MAX_MANAGER_NODES"`
	defaultMinWorkerNodes  uint64 `envconfig:"DEFAULT_MIN_WORKER_NODES"`
	defaultMaxWorkerNodes  uint64 `envconfig:"DEFAULT_MAX_WORKER_NODES"`
}

// NewAWSScalerFromEnv creates an AWS based node scaler
func NewAWSScalerFromEnv() (*AWSScaler, error) {

	envFile := os.Getenv("AWS_ENV_FILE")
	if len(envFile) == 0 {
		return nil, fmt.Errorf("AWS_ENV_FILE not defined")
	}

	godotenv.Load(envFile)

	var spec AWSSpec
	err := envconfig.Process("", &spec)
	if err != nil {
		return nil, errors.Wrap(err, "Unable to get process env vars")
	}

	sess, err := session.NewSession()
	if err != nil {
		return nil, errors.Wrap(err, "Unable to create aws session")
	}
	svc := autoscaling.New(sess, aws.NewConfig().WithRegion(spec.region))
	return &AWSScaler{
		svc:  svc,
		spec: spec,
	}, nil
}

// ScaleWorkerByDelta scales aws worker nodes by delta
func (s *AWSScaler) ScaleWorkerByDelta(delta int) (uint64, uint64, error) {
	return s.scaleNodes(delta, s.spec.workerConfigName, int64(s.spec.defaultMinWorkerNodes),
		int64(s.spec.defaultMaxWorkerNodes))
}

// ScaleManagerByDelta scales aws manager nodes by delta
func (s *AWSScaler) ScaleManagerByDelta(delta int) (uint64, uint64, error) {
	return s.scaleNodes(delta, s.spec.managerConfigName, int64(s.spec.defaultMinManagerNodes),
		int64(s.spec.defaultMaxManagerNodes))
}

func (s *AWSScaler) scaleNodes(delta int, configName string, minSize int64, maxSize int64) (uint64, uint64, error) {

	groupsOutput, err := s.svc.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{})
	if err != nil {
		return 0, 0, errors.Wrap(err, "Unable to describe-auto-scaling-groups")
	}

	var targetGroup *autoscaling.Group
	for _, group := range groupsOutput.AutoScalingGroups {
		if *group.LaunchConfigurationName == configName {
			targetGroup = group
			break
		}
	}
	if targetGroup == nil {
		return 0, 0, fmt.Errorf("Unable to find launch-configuration-name: %s", configName)
	}

	currentCapacity := *targetGroup.DesiredCapacity
	newCapacity := currentCapacity + int64(delta)
	if newCapacity < 0 {
		newCapacity = 0
	}

	// Check if newCapacity is in bounds
	if newCapacity < minSize || newCapacity > maxSize {
		return 0, 0, fmt.Errorf("New capacity: %d is not in between %d and %d", newCapacity, minSize, maxSize)
	}

	_, err = s.svc.UpdateAutoScalingGroup(&autoscaling.UpdateAutoScalingGroupInput{DesiredCapacity: &newCapacity,
		MinSize: &minSize,
		MaxSize: &maxSize})
	if err != nil {
		return 0, 0, errors.Wrap(err,
			fmt.Sprintf("Error calling update-auto-scaling-group with desired-capacity: %d", newCapacity))
	}
	return uint64(currentCapacity), uint64(newCapacity), nil
}
<|endoftext|>


<commit_before>package goovs

import (
	"fmt"

	"github.com/kopwei/libovsdb"
)

// OvsBridge is the structure represents the ovs bridge
type OvsBridge struct {
	UUID       string   `json:"_uuid"`
	Controller string   `json:"controller"`
	Name       string   `json:"name"`
	PortUUIDs  []string `json:"ports"`
	DatapathID string   `json:"datapath_id"`
}

// ReadFromDBRow is used to initialize the object from a row
func (bridge *OvsBridge) ReadFromDBRow(row *libovsdb.Row) error {
	if bridge.PortUUIDs == nil {
		bridge.PortUUIDs = make([]string, 0)
	}
	for field, value := range row.Fields {
		switch field {
		case "name":
			bridge.Name = value.(string)
		case "datapath_id":
			switch value.(type) {
			case string:
				bridge.DatapathID = value.(string)
			}
		case "ports":
			switch value.(type) {
			case libovsdb.UUID:
				bridge.PortUUIDs = append(bridge.PortUUIDs, value.(libovsdb.UUID).GoUuid)
			case libovsdb.OvsSet:
				for _, uuids := range value.(libovsdb.OvsSet).GoSet {
					bridge.PortUUIDs = append(bridge.PortUUIDs, uuids.(libovsdb.UUID).GoUuid)
				}
			}
		}
	}
	return nil
}

// CreateBridge is used to create a ovs bridge
func (client *ovsClient) CreateBridge(brname string) error {
	bridgeUpdateLock.Lock()
	defer bridgeUpdateLock.Unlock()
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if bridgeExists {
		return nil
	}

	namedBridgeUUID := "gobridge"
	namedPortUUID := "goport"
	namedInterfaceUUID := "gointerface"

	// intf row to insert
	intf := make(map[string]interface{})
	intf["name"] = brname
	intf["type"] = `internal`

	insertInterfaceOp := libovsdb.Operation{
		Op:       "insert",
		Table:    "Interface",
		Row:      intf,
		UUIDName: namedInterfaceUUID,
	}

	// port row to insert
	port := make(map[string]interface{})
	port["name"] = brname
	port["interfaces"] = libovsdb.UUID{GoUuid: namedInterfaceUUID}

	insertPortOp := libovsdb.Operation{
		Op:       insertOperation,
		Table:    portTableName,
		Row:      port,
		UUIDName: namedPortUUID,
	}

	// bridge row to insert
	bridge := make(map[string]interface{})
	bridge["name"] = brname
	bridge["stp_enable"] = false
	bridge["ports"] = libovsdb.UUID{GoUuid: namedPortUUID}

	// simple insert operation
	insertBridgeOp := libovsdb.Operation{
		Op:       insertOperation,
		Table:    bridgeTableName,
		Row:      bridge,
		UUIDName: namedBridgeUUID,
	}

	// Inserting a Bridge row in Bridge table requires mutating the open_vswitch table
	mutateUUID := []libovsdb.UUID{libovsdb.UUID{GoUuid: namedBridgeUUID}}
	mutateSet, _ := libovsdb.NewOvsSet(mutateUUID)
	mutation := libovsdb.NewMutation("bridges", insertOperation, mutateSet)
	condition := libovsdb.NewCondition("_uuid", "==", libovsdb.UUID{GoUuid: getRootUUID()})

	// simple mutate operation
	mutateOp := libovsdb.Operation{
		Op:        mutateOperation,
		Table:     ovsTableName,
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []libovsdb.Operation{insertInterfaceOp, insertPortOp, insertBridgeOp, mutateOp}
	return client.transact(operations, "create bridge")
}

// DeleteBridge is used to delete a ovs bridge
func (client *ovsClient) DeleteBridge(brname string) error {
	bridgeUpdateLock.Lock()
	defer bridgeUpdateLock.Unlock()
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if !bridgeExists {
		return nil
	}
	bridgeUUID, err := client.getBridgeUUIDByName(brname)
	if err != nil {
		return err
	}

	delBridgeCondition := libovsdb.NewCondition("name", "==", brname)
	deleteOp := libovsdb.Operation{
		Op:    deleteOperation,
		Table: bridgeTableName,
		Where: []interface{}{delBridgeCondition},
	}

	// Deleting a Bridge row in Bridge table requires mutating the open_vswitch table
	mutateUUID := []libovsdb.UUID{libovsdb.UUID{GoUuid: bridgeUUID}}
	mutateSet, _ := libovsdb.NewOvsSet(mutateUUID)
	mutation := libovsdb.NewMutation("bridges", deleteOperation, mutateSet)
	condition := libovsdb.NewCondition("_uuid", "==", libovsdb.UUID{GoUuid: getRootUUID()})

	// simple mutate operation
	mutateOp := libovsdb.Operation{
		Op:        mutateOperation,
		Table:     ovsTableName,
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []libovsdb.Operation{deleteOp, mutateOp}
	return client.transact(operations, "delete bridge")
}

func (client *ovsClient) deleteAllPortsOnBridge(brname string) error {
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if !bridgeExists {
		return nil
	}

	portList, err := client.findAllPortUUIDsOnBridge(brname)
	if err != nil {
		return err
	}
	if len(portList) != 0 {
		for _, portUUID := range portList {
			err = client.deletePortByUUID(brname, portUUID)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// BridgeExists is used to check if a bridge exists or not
func (client *ovsClient) BridgeExists(brname string) (bool, error) {
	// if bridge name is invalid, return false
	if brname == "" {
		return false, fmt.Errorf("The bridge name is invalid")
	}
	bridgeCacheUpdateLock.RLock()
	defer bridgeCacheUpdateLock.RUnlock()
	for _, br := range client.bridgeCache {
		if br.Name == brname {
			return true, nil
		}
	}
	return false, nil
}

func (client *ovsClient) UpdateBridgeController(brname, controller string) error {
	bridgeUpdateLock.Lock()
	defer bridgeUpdateLock.Unlock()
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if !bridgeExists {
		return nil
	}

	bridgeUUID, err := client.getBridgeUUIDByName(brname)
	if err != nil {
		return err
	}

	namedControllerUUID := "gocontroller"
	ctrler := make(map[string]interface{})
	ctrler["target"] = controller

	insertControllerOp := libovsdb.Operation{
		Op:       insertOperation,
		Table:    controllerTableName,
		Row:      ctrler,
		UUIDName: namedControllerUUID,
	}
	bridge := make(map[string]interface{})
	bridge["controller"] = libovsdb.UUID{GoUuid: namedControllerUUID}

	updateBrCondition := libovsdb.NewCondition("name", "==", brname)
	updateOp := libovsdb.Operation{
		Op:    updateOperation,
		Table: bridgeTableName,
		Row:   bridge,
		Where: []interface{}{updateBrCondition},
	}

	// Update a Bridge row in Bridge table requires mutating the open_vswitch table
	mutateUUID := []libovsdb.UUID{libovsdb.UUID{GoUuid: namedControllerUUID}}
	mutateSet, _ := libovsdb.NewOvsSet(mutateUUID)
	mutation := libovsdb.NewMutation("controller", insertOperation, mutateSet)
	condition := libovsdb.NewCondition("_uuid", "==", libovsdb.UUID{GoUuid: bridgeUUID})

	// simple mutate operation
	mutateOp := libovsdb.Operation{
		Op:        mutateOperation,
		Table:     bridgeTableName,
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []libovsdb.Operation{insertControllerOp, updateOp, mutateOp}
	return client.transact(operations, "update bridge controller")
}

func (client *ovsClient) getBridgeUUIDByName(brname string) (string, error) {
	condition := libovsdb.NewCondition("name", "==", brname)
	selectOp := libovsdb.Operation{
		Op:    selectOperation,
		Table: bridgeTableName,
		Where: []interface{}{condition},
		//Columns: []string{"_uuid"},
	}
	operations := []libovsdb.Operation{selectOp}
	reply, _ := client.dbClient.Transact(defaultOvsDB, operations...)

	if len(reply) < len(operations) {
		return "", fmt.Errorf("get bridge uuid failed due to Number of Replies should be at least equal to number of Operations")
	}
	if reply[0].Error != "" {
		return "", fmt.Errorf("get bridge uuid failed due to Transaction Failed due to an error: %v", reply[0].Error)
	}
	if len(reply[0].Rows) == 0 {
		return "", fmt.Errorf("get bridge uuid failed due to bridge name doesn't exist")
	}
	answer := reply[0].Rows[0]["_uuid"].([]interface{})[1].(string)
	return answer, nil
}
<commit_msg>Use cache in bridge file<commit_after>package goovs

import (
	"fmt"

	"github.com/kopwei/libovsdb"
)

// OvsBridge is the structure represents the ovs bridge
type OvsBridge struct {
	UUID       string   `json:"_uuid"`
	Controller string   `json:"controller"`
	Name       string   `json:"name"`
	PortUUIDs  []string `json:"ports"`
	DatapathID string   `json:"datapath_id"`
}

// ReadFromDBRow is used to initialize the object from a row
func (bridge *OvsBridge) ReadFromDBRow(row *libovsdb.Row) error {
	if bridge.PortUUIDs == nil {
		bridge.PortUUIDs = make([]string, 0)
	}
	for field, value := range row.Fields {
		switch field {
		case "name":
			bridge.Name = value.(string)
		case "datapath_id":
			switch value.(type) {
			case string:
				bridge.DatapathID = value.(string)
			}
		case "ports":
			switch value.(type) {
			case libovsdb.UUID:
				bridge.PortUUIDs = append(bridge.PortUUIDs, value.(libovsdb.UUID).GoUuid)
			case libovsdb.OvsSet:
				for _, uuids := range value.(libovsdb.OvsSet).GoSet {
					bridge.PortUUIDs = append(bridge.PortUUIDs, uuids.(libovsdb.UUID).GoUuid)
				}
			}
		}
	}
	return nil
}

// CreateBridge is used to create a ovs bridge
func (client *ovsClient) CreateBridge(brname string) error {
	bridgeUpdateLock.Lock()
	defer bridgeUpdateLock.Unlock()
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if bridgeExists {
		return nil
	}

	namedBridgeUUID := "gobridge"
	namedPortUUID := "goport"
	namedInterfaceUUID := "gointerface"

	// intf row to insert
	intf := make(map[string]interface{})
	intf["name"] = brname
	intf["type"] = `internal`

	insertInterfaceOp := libovsdb.Operation{
		Op:       "insert",
		Table:    "Interface",
		Row:      intf,
		UUIDName: namedInterfaceUUID,
	}

	// port row to insert
	port := make(map[string]interface{})
	port["name"] = brname
	port["interfaces"] = libovsdb.UUID{GoUuid: namedInterfaceUUID}

	insertPortOp := libovsdb.Operation{
		Op:       insertOperation,
		Table:    portTableName,
		Row:      port,
		UUIDName: namedPortUUID,
	}

	// bridge row to insert
	bridge := make(map[string]interface{})
	bridge["name"] = brname
	bridge["stp_enable"] = false
	bridge["ports"] = libovsdb.UUID{GoUuid: namedPortUUID}

	// simple insert operation
	insertBridgeOp := libovsdb.Operation{
		Op:       insertOperation,
		Table:    bridgeTableName,
		Row:      bridge,
		UUIDName: namedBridgeUUID,
	}

	// Inserting a Bridge row in Bridge table requires mutating the open_vswitch table
	mutateUUID := []libovsdb.UUID{libovsdb.UUID{GoUuid: namedBridgeUUID}}
	mutateSet, _ := libovsdb.NewOvsSet(mutateUUID)
	mutation := libovsdb.NewMutation("bridges", insertOperation, mutateSet)
	condition := libovsdb.NewCondition("_uuid", "==", libovsdb.UUID{GoUuid: getRootUUID()})

	// simple mutate operation
	mutateOp := libovsdb.Operation{
		Op:        mutateOperation,
		Table:     ovsTableName,
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []libovsdb.Operation{insertInterfaceOp, insertPortOp, insertBridgeOp, mutateOp}
	return client.transact(operations, "create bridge")
}

// DeleteBridge is used to delete a ovs bridge
func (client *ovsClient) DeleteBridge(brname string) error {
	bridgeUpdateLock.Lock()
	defer bridgeUpdateLock.Unlock()
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if !bridgeExists {
		return nil
	}
	bridgeUUID, err := client.getBridgeUUIDByName(brname)
	if err != nil {
		return err
	}

	delBridgeCondition := libovsdb.NewCondition("name", "==", brname)
	deleteOp := libovsdb.Operation{
		Op:    deleteOperation,
		Table: bridgeTableName,
		Where: []interface{}{delBridgeCondition},
	}

	// Deleting a Bridge row in Bridge table requires mutating the open_vswitch table
	mutateUUID := []libovsdb.UUID{libovsdb.UUID{GoUuid: bridgeUUID}}
	mutateSet, _ := libovsdb.NewOvsSet(mutateUUID)
	mutation := libovsdb.NewMutation("bridges", deleteOperation, mutateSet)
	condition := libovsdb.NewCondition("_uuid", "==", libovsdb.UUID{GoUuid: getRootUUID()})

	// simple mutate operation
	mutateOp := libovsdb.Operation{
		Op:        mutateOperation,
		Table:     ovsTableName,
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []libovsdb.Operation{deleteOp, mutateOp}
	return client.transact(operations, "delete bridge")
}

func (client *ovsClient) deleteAllPortsOnBridge(brname string) error {
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if !bridgeExists {
		return nil
	}

	portList, err := client.findAllPortUUIDsOnBridge(brname)
	if err != nil {
		return err
	}
	if len(portList) != 0 {
		for _, portUUID := range portList {
			err = client.deletePortByUUID(brname, portUUID)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// BridgeExists is used to check if a bridge exists or not
func (client *ovsClient) BridgeExists(brname string) (bool, error) {
	// if bridge name is invalid, return false
	if brname == "" {
		return false, fmt.Errorf("The bridge name is invalid")
	}
	bridgeCacheUpdateLock.RLock()
	defer bridgeCacheUpdateLock.RUnlock()
	for _, br := range client.bridgeCache {
		if br.Name == brname {
			return true, nil
		}
	}
	return false, nil
}

func (client *ovsClient) UpdateBridgeController(brname, controller string) error {
	bridgeUpdateLock.Lock()
	defer bridgeUpdateLock.Unlock()
	bridgeExists, err := client.BridgeExists(brname)
	if err != nil {
		return fmt.Errorf("Failed to retrieve the bridge info")
	} else if !bridgeExists {
		return nil
	}

	bridgeUUID, err := client.getBridgeUUIDByName(brname)
	if err != nil {
		return err
	}

	namedControllerUUID := "gocontroller"
	ctrler := make(map[string]interface{})
	ctrler["target"] = controller

	insertControllerOp := libovsdb.Operation{
		Op:       insertOperation,
		Table:    controllerTableName,
		Row:      ctrler,
		UUIDName: namedControllerUUID,
	}
	bridge := make(map[string]interface{})
	bridge["controller"] = libovsdb.UUID{GoUuid: namedControllerUUID}

	updateBrCondition := libovsdb.NewCondition("name", "==", brname)
	updateOp := libovsdb.Operation{
		Op:    updateOperation,
		Table: bridgeTableName,
		Row:   bridge,
		Where: []interface{}{updateBrCondition},
	}

	// Update a Bridge row in Bridge table requires mutating the open_vswitch table
	mutateUUID := []libovsdb.UUID{libovsdb.UUID{GoUuid: namedControllerUUID}}
	mutateSet, _ := libovsdb.NewOvsSet(mutateUUID)
	mutation := libovsdb.NewMutation("controller", insertOperation, mutateSet)
	condition := libovsdb.NewCondition("_uuid", "==", libovsdb.UUID{GoUuid: bridgeUUID})

	// simple mutate operation
	mutateOp := libovsdb.Operation{
		Op:        mutateOperation,
		Table:     bridgeTableName,
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []libovsdb.Operation{insertControllerOp, updateOp, mutateOp}
	return client.transact(operations, "update bridge controller")
}

func (client *ovsClient) getBridgeUUIDByName(brname string) (string, error) {
	if brname == "" {
		return "", fmt.Errorf("The bridge name is invalid")
	}
	for uuid, bridge := range client.bridgeCache {
		if bridge.Name == brname {
			return uuid, nil
		}
	}
	return "", fmt.Errorf("The brdige name %s doesn't exist", brname)
}
<|endoftext|>


<commit_before>package goovs

import (
	"fmt"
	"net"
	"reflect"
	"strconv"
	"sync"

	"github.com/kopwei/libovsdb"
)

const (
	defaultTCPHost      = "127.0.0.1"
	defaultTCPPort      = 6640
	defaultUnixEndpoint = "/var/run/openvswitch/db.sock"
)

const (
	defaultOvsDB = "Open_vSwitch"
)

const (
	ovsTableName        = "Open_vSwitch"
	bridgeTableName     = "Bridge"
	portTableName       = "Port"
	interfaceTableName  = "Interface"
	controllerTableName = "Controller"
	//flowTableName = ""
)

const (
	deleteOperation = "delete"
	insertOperation = "insert"
	mutateOperation = "mutate"
	selectOperation = "select"
	updateOperation = "update"
)

// OvsObject is the main interface represent an ovs object
type OvsObject interface {
	ReadFromDBRow(row *libovsdb.Row) error
}

// OvsClient is the interface towards outside user
type OvsClient interface {
	BridgeExists(brname string) (bool, error)
	CreateBridge(brname string) error
	DeleteBridge(brname string) error
	UpdateBridgeController(brname, controller string) error
	CreateInternalPort(brname, portname string, vlantag int) error
	CreateVethPort(brname, portname string, vlantag int) error
	CreatePatchPort(brname, portname, peername string) error
	DeletePort(brname, porname string) error
	UpdatePortTagByName(brname, portname string, vlantag int) error
	FindAllPortsOnBridge(brname string) ([]string, error)
	PortExistsOnBridge(portname, brname string) (bool, error)
	RemoveInterfaceFromPort(portname, interfaceUUID string) error
	Disconnect()
}

type ovsClient struct {
	dbClient       *libovsdb.OvsdbClient
	bridgeCache    map[string]*OvsBridge
	portCache      map[string]*OvsPort
	interfaceCache map[string]*OvsInterface
}

var client *ovsClient
var update chan *libovsdb.TableUpdates
var cache map[string]map[string]libovsdb.Row
var ovsclient *ovsClient

var bridgeUpdateLock sync.RWMutex
var portUpdateLock sync.RWMutex
var intfUpdateLock sync.RWMutex

var bridgeCacheUpdateLock sync.RWMutex
var portCacheUpdateLock sync.RWMutex
var intfCacheUpdateLock sync.RWMutex

// GetOVSClient is used for
func GetOVSClient(contype, endpoint string) (OvsClient, error) {
	if client != nil {
		return client, nil
	}
	var dbclient *libovsdb.OvsdbClient
	var err error
	if contype == "tcp" {
		if endpoint == "" {
			dbclient, err = libovsdb.Connect(defaultTCPHost, defaultTCPPort)
		} else {
			host, port, err := net.SplitHostPort(endpoint)
			if err != nil {
				return nil, err
			}
			portInt, _ := strconv.Atoi(port)
			dbclient, err = libovsdb.Connect(host, portInt)
		}
	} else if contype == "unix" {
		if endpoint == "" {
			endpoint = defaultUnixEndpoint
		}
		dbclient, err = libovsdb.ConnectUnix(endpoint)
	}
	if err != nil {
		return nil, err
	}
	var notfr notifier
	dbclient.Register(notfr)

	update = make(chan *libovsdb.TableUpdates)
	cache = make(map[string]map[string]libovsdb.Row)

	client = &ovsClient{dbClient: dbclient}
	if client.bridgeCache == nil {
		client.bridgeCache = make(map[string]*OvsBridge)
	}
	if client.portCache == nil {
		client.portCache = make(map[string]*OvsPort)
	}
	if client.interfaceCache == nil {
		client.interfaceCache = make(map[string]*OvsInterface)
	}

	initial, _ := dbclient.MonitorAll(defaultOvsDB, "")
	populateCache(*initial)
	return client, nil
}

func (client *ovsClient) Disconnect() {
	client.dbClient.Disconnect()
}

func (client *ovsClient) transact(operations []libovsdb.Operation, action string) error {
	reply, _ := client.dbClient.Transact(defaultOvsDB, operations...)

	if len(reply) < len(operations) {
		return fmt.Errorf("%s failed due to Number of Replies should be at least equal to number of Operations", action)
	}
	//ok := true
	for i, o := range reply {
		if o.Error != "" {
			//ok = false
			if i < len(operations) {
				return fmt.Errorf("%s transaction Failed due to an error : %s details: %s in %+v", action, o.Error, o.Details, operations[i])
			}
			return fmt.Errorf("%s transaction Failed due to an error :%s", action, o.Error)
		}
	}
	//if ok {
	//	log.Println(action, "successful: ", reply[0].UUID.GoUuid)
	//}

	return nil
}

type notifier struct {
}

func (n notifier) Update(context interface{}, tableUpdates libovsdb.TableUpdates) {
	populateCache(tableUpdates)
	update <- &tableUpdates
}
func (n notifier) Locked([]interface{}) {
}
func (n notifier) Stolen([]interface{}) {
}
func (n notifier) Echo([]interface{}) {
}
func (n notifier) Disconnect([]interface{}) {
}
func (n notifier) Disconnected(*libovsdb.OvsdbClient) {
}

func (client *ovsClient) updateOvsObjCacheByRow(objtype, uuid string, row *libovsdb.Row) error {
	switch objtype {
	case bridgeTableName:
		brObj := &OvsBridge{UUID: uuid}
		brObj.ReadFromDBRow(row)
		bridgeCacheUpdateLock.Lock()
		client.bridgeCache[uuid] = brObj
		bridgeCacheUpdateLock.Unlock()
		//data, _ := json.MarshalIndent(brObj, "", "    ")
		//fmt.Println(string(data))
	case portTableName:
		portObj := &OvsPort{UUID: uuid}
		portObj.ReadFromDBRow(row)
		portCacheUpdateLock.Lock()
		client.portCache[uuid] = portObj
		portCacheUpdateLock.Unlock()
		//data, _ := json.MarshalIndent(portObj, "", "    ")
		//fmt.Println(string(data))
	case interfaceTableName:
		intfObj := &OvsInterface{UUID: uuid}
		intfObj.ReadFromDBRow(row)
		intfCacheUpdateLock.Lock()
		client.interfaceCache[uuid] = intfObj
		intfCacheUpdateLock.Unlock()
		//data, _ := json.MarshalIndent(intfObj, "", "    ")
		//fmt.Println(string(data))
	}
	return nil
}

func (client *ovsClient) removeOvsObjCacheByRow(objtype, uuid string) error {
	switch objtype {
	case bridgeTableName:
		if _, ok := client.bridgeCache[uuid]; ok {
			bridgeCacheUpdateLock.Lock()
			delete(client.bridgeCache, uuid)
			bridgeCacheUpdateLock.Unlock()
		}
	case portTableName:
		if _, ok := client.portCache[uuid]; ok {
			portCacheUpdateLock.Lock()
			delete(client.portCache, uuid)
			portCacheUpdateLock.Unlock()
		}
	case interfaceTableName:
		if _, ok := client.interfaceCache[uuid]; ok {
			intfCacheUpdateLock.Lock()
			delete(client.interfaceCache, uuid)
			intfCacheUpdateLock.Unlock()
		}
	}
	return nil
}

func populateCache(updates libovsdb.TableUpdates) {
	for table, tableUpdate := range updates.Updates {
		if _, ok := cache[table]; !ok {
			cache[table] = make(map[string]libovsdb.Row)

		}
		for uuid, row := range tableUpdate.Rows {
			empty := libovsdb.Row{}
			if !reflect.DeepEqual(row.New, empty) {
				// fmt.Println(table + " with uuid " + uuid + "is updated")
				cache[table][uuid] = row.New
				client.updateOvsObjCacheByRow(table, uuid, &row.New)
			} else {
				delete(cache[table], uuid)
				// fmt.Println(table + " with uuid " + uuid + "is removed")
				client.removeOvsObjCacheByRow(table, uuid)
			}
		}
	}
}

func getRootUUID() string {
	for uuid := range cache[defaultOvsDB] {
		return uuid
	}
	return ""
}
<commit_msg>removed update channel from ovsclient<commit_after>package goovs

import (
	"fmt"
	"net"
	"reflect"
	"strconv"
	"sync"

	"github.com/kopwei/libovsdb"
)

const (
	defaultTCPHost      = "127.0.0.1"
	defaultTCPPort      = 6640
	defaultUnixEndpoint = "/var/run/openvswitch/db.sock"
)

const (
	defaultOvsDB = "Open_vSwitch"
)

const (
	ovsTableName        = "Open_vSwitch"
	bridgeTableName     = "Bridge"
	portTableName       = "Port"
	interfaceTableName  = "Interface"
	controllerTableName = "Controller"
	//flowTableName = ""
)

const (
	deleteOperation = "delete"
	insertOperation = "insert"
	mutateOperation = "mutate"
	selectOperation = "select"
	updateOperation = "update"
)

// OvsObject is the main interface represent an ovs object
type OvsObject interface {
	ReadFromDBRow(row *libovsdb.Row) error
}

// OvsClient is the interface towards outside user
type OvsClient interface {
	BridgeExists(brname string) (bool, error)
	CreateBridge(brname string) error
	DeleteBridge(brname string) error
	UpdateBridgeController(brname, controller string) error
	CreateInternalPort(brname, portname string, vlantag int) error
	CreateVethPort(brname, portname string, vlantag int) error
	CreatePatchPort(brname, portname, peername string) error
	DeletePort(brname, porname string) error
	UpdatePortTagByName(brname, portname string, vlantag int) error
	FindAllPortsOnBridge(brname string) ([]string, error)
	PortExistsOnBridge(portname, brname string) (bool, error)
	RemoveInterfaceFromPort(portname, interfaceUUID string) error
	Disconnect()
}

type ovsClient struct {
	dbClient       *libovsdb.OvsdbClient
	bridgeCache    map[string]*OvsBridge
	portCache      map[string]*OvsPort
	interfaceCache map[string]*OvsInterface
}

var client *ovsClient
var cache map[string]map[string]libovsdb.Row
var ovsclient *ovsClient

var bridgeUpdateLock sync.RWMutex
var portUpdateLock sync.RWMutex
var intfUpdateLock sync.RWMutex

var bridgeCacheUpdateLock sync.RWMutex
var portCacheUpdateLock sync.RWMutex
var intfCacheUpdateLock sync.RWMutex

// GetOVSClient is used for
func GetOVSClient(contype, endpoint string) (OvsClient, error) {
	if client != nil {
		return client, nil
	}
	var dbclient *libovsdb.OvsdbClient
	var err error
	if contype == "tcp" {
		if endpoint == "" {
			dbclient, err = libovsdb.Connect(defaultTCPHost, defaultTCPPort)
		} else {
			host, port, err := net.SplitHostPort(endpoint)
			if err != nil {
				return nil, err
			}
			portInt, _ := strconv.Atoi(port)
			dbclient, err = libovsdb.Connect(host, portInt)
		}
	} else if contype == "unix" {
		if endpoint == "" {
			endpoint = defaultUnixEndpoint
		}
		dbclient, err = libovsdb.ConnectUnix(endpoint)
	}
	if err != nil {
		return nil, err
	}
	var notfr notifier
	dbclient.Register(notfr)

	cache = make(map[string]map[string]libovsdb.Row)

	client = &ovsClient{dbClient: dbclient}
	if client.bridgeCache == nil {
		client.bridgeCache = make(map[string]*OvsBridge)
	}
	if client.portCache == nil {
		client.portCache = make(map[string]*OvsPort)
	}
	if client.interfaceCache == nil {
		client.interfaceCache = make(map[string]*OvsInterface)
	}

	initial, _ := dbclient.MonitorAll(defaultOvsDB, "")
	populateCache(*initial)
	return client, nil
}

func (client *ovsClient) Disconnect() {
	client.dbClient.Disconnect()
}

func (client *ovsClient) transact(operations []libovsdb.Operation, action string) error {
	reply, _ := client.dbClient.Transact(defaultOvsDB, operations...)

	if len(reply) < len(operations) {
		return fmt.Errorf("%s failed due to Number of Replies should be at least equal to number of Operations", action)
	}
	//ok := true
	for i, o := range reply {
		if o.Error != "" {
			//ok = false
			if i < len(operations) {
				return fmt.Errorf("%s transaction Failed due to an error : %s details: %s in %+v", action, o.Error, o.Details, operations[i])
			}
			return fmt.Errorf("%s transaction Failed due to an error :%s", action, o.Error)
		}
	}
	//if ok {
	//	log.Println(action, "successful: ", reply[0].UUID.GoUuid)
	//}

	return nil
}

type notifier struct {
}

func (n notifier) Update(context interface{}, tableUpdates libovsdb.TableUpdates) {
	populateCache(tableUpdates)
}
func (n notifier) Locked([]interface{}) {
}
func (n notifier) Stolen([]interface{}) {
}
func (n notifier) Echo([]interface{}) {
}
func (n notifier) Disconnect([]interface{}) {
}
func (n notifier) Disconnected(*libovsdb.OvsdbClient) {
}

func (client *ovsClient) updateOvsObjCacheByRow(objtype, uuid string, row *libovsdb.Row) error {
	switch objtype {
	case bridgeTableName:
		brObj := &OvsBridge{UUID: uuid}
		brObj.ReadFromDBRow(row)
		bridgeCacheUpdateLock.Lock()
		client.bridgeCache[uuid] = brObj
		bridgeCacheUpdateLock.Unlock()
		//data, _ := json.MarshalIndent(brObj, "", "    ")
		//fmt.Println(string(data))
	case portTableName:
		portObj := &OvsPort{UUID: uuid}
		portObj.ReadFromDBRow(row)
		portCacheUpdateLock.Lock()
		client.portCache[uuid] = portObj
		portCacheUpdateLock.Unlock()
		//data, _ := json.MarshalIndent(portObj, "", "    ")
		//fmt.Println(string(data))
	case interfaceTableName:
		intfObj := &OvsInterface{UUID: uuid}
		intfObj.ReadFromDBRow(row)
		intfCacheUpdateLock.Lock()
		client.interfaceCache[uuid] = intfObj
		intfCacheUpdateLock.Unlock()
		//data, _ := json.MarshalIndent(intfObj, "", "    ")
		//fmt.Println(string(data))
	}
	return nil
}

func (client *ovsClient) removeOvsObjCacheByRow(objtype, uuid string) error {
	switch objtype {
	case bridgeTableName:
		if _, ok := client.bridgeCache[uuid]; ok {
			bridgeCacheUpdateLock.Lock()
			delete(client.bridgeCache, uuid)
			bridgeCacheUpdateLock.Unlock()
		}
	case portTableName:
		if _, ok := client.portCache[uuid]; ok {
			portCacheUpdateLock.Lock()
			delete(client.portCache, uuid)
			portCacheUpdateLock.Unlock()
		}
	case interfaceTableName:
		if _, ok := client.interfaceCache[uuid]; ok {
			intfCacheUpdateLock.Lock()
			delete(client.interfaceCache, uuid)
			intfCacheUpdateLock.Unlock()
		}
	}
	return nil
}

func populateCache(updates libovsdb.TableUpdates) {
	for table, tableUpdate := range updates.Updates {
		if _, ok := cache[table]; !ok {
			cache[table] = make(map[string]libovsdb.Row)

		}
		for uuid, row := range tableUpdate.Rows {
			empty := libovsdb.Row{}
			if !reflect.DeepEqual(row.New, empty) {
				// fmt.Println(table + " with uuid " + uuid + "is updated")
				cache[table][uuid] = row.New
				client.updateOvsObjCacheByRow(table, uuid, &row.New)
			} else {
				delete(cache[table], uuid)
				// fmt.Println(table + " with uuid " + uuid + "is removed")
				client.removeOvsObjCacheByRow(table, uuid)
			}
		}
	}
}

func getRootUUID() string {
	for uuid := range cache[defaultOvsDB] {
		return uuid
	}
	return ""
}
<|endoftext|>


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

package p

import "once"
import "sync"

type osUser struct {
	uid int
}

type osUsers struct {
	users  map[int]*osUser
	groups map[int]*osGroup
	sync.Mutex
}

// Simple Users implementation that fakes looking up users and groups
// by uid only. The names and groups memberships are empty
var OsUsers *osUsers

func (u *osUser) Name() string { return "" }

func (u *osUser) Id() int { return u.uid }

func (u *osUser) Groups() []Group { return nil }

type osGroup struct {
	gid int
}

func (g *osGroup) Name() string { return "" }

func (g *osGroup) Id() int { return g.gid }

func (g *osGroup) Members() []User { return nil }

func initOsusers() {
	OsUsers = new(osUsers)
	OsUsers.users = make(map[int]*osUser)
	OsUsers.groups = make(map[int]*osGroup)
}

func (up *osUsers) Uid2User(uid int) User {
	once.Do(initOsusers)
	OsUsers.Lock()
	user, present := OsUsers.users[uid]
	if present {
		OsUsers.Unlock()
		return user
	}

	user = new(osUser)
	user.uid = uid
	OsUsers.users[uid] = user
	OsUsers.Unlock()
	return user
}

func (up *osUsers) Uname2User(uname string) User {
	return nil
}

func (up *osUsers) Gid2Group(gid int) Group {
	once.Do(initOsusers)
	OsUsers.Lock()
	group, present := OsUsers.groups[gid]
	if present {
		OsUsers.Unlock()
		return group
	}

	group = new(osGroup)
	group.gid = gid
	OsUsers.groups[gid] = group
	OsUsers.Unlock()
	return group
}

func (up *osUsers) Gname2Group(gname string) Group {
	return nil
}
<commit_msg>change for once disappearing.<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package p

import "sync"

var once sync.Once

type osUser struct {
	uid int
}

type osUsers struct {
	users  map[int]*osUser
	groups map[int]*osGroup
	sync.Mutex
}

// Simple Users implementation that fakes looking up users and groups
// by uid only. The names and groups memberships are empty
var OsUsers *osUsers

func (u *osUser) Name() string { return "" }

func (u *osUser) Id() int { return u.uid }

func (u *osUser) Groups() []Group { return nil }

type osGroup struct {
	gid int
}

func (g *osGroup) Name() string { return "" }

func (g *osGroup) Id() int { return g.gid }

func (g *osGroup) Members() []User { return nil }

func initOsusers() {
	OsUsers = new(osUsers)
	OsUsers.users = make(map[int]*osUser)
	OsUsers.groups = make(map[int]*osGroup)
}

func (up *osUsers) Uid2User(uid int) User {
	once.Do(initOsusers)
	OsUsers.Lock()
	user, present := OsUsers.users[uid]
	if present {
		OsUsers.Unlock()
		return user
	}

	user = new(osUser)
	user.uid = uid
	OsUsers.users[uid] = user
	OsUsers.Unlock()
	return user
}

func (up *osUsers) Uname2User(uname string) User {
	return nil
}

func (up *osUsers) Gid2Group(gid int) Group {
	once.Do(initOsusers)
	OsUsers.Lock()
	group, present := OsUsers.groups[gid]
	if present {
		OsUsers.Unlock()
		return group
	}

	group = new(osGroup)
	group.gid = gid
	OsUsers.groups[gid] = group
	OsUsers.Unlock()
	return group
}

func (up *osUsers) Gname2Group(gname string) Group {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"time"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/metrics2"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
)

const (
	livenessMetric       = "k8s_deployer"
	gitstoreSubscriberID = "k8s-deployer"
)

func main() {
	cluster := flag.String("cluster", "main", "GKE cluster name")
	configRepo := flag.String("config_repo", "https://skia.googlesource.com/k8s-config.git", "Repo containing Kubernetes configurations.")
	configSubdir := flag.String("config_subdir", "", "Subdirectory within the config repo to apply to this cluster.")
	interval := flag.Duration("interval", 10*time.Minute, "How often to re-apply configurations to the cluster")
	promPort := flag.String("prom_port", ":20000", "Metrics service address (e.g., ':20000')")

	common.InitWithMust("k8s_deployer", common.PrometheusOpt(promPort))
	defer sklog.Flush()

	if *cluster == "" {
		sklog.Fatal("cluster is required.")
	}
	if *configRepo == "" {
		sklog.Fatal("config_repo is required.")
	}
	if *configSubdir == "" {
		// Note: this wouldn't be required if we had separate config repos per
		// cluster.
		sklog.Fatal("config_subdir is required.")
	}

	ctx := context.Background()

	// OAuth2.0 TokenSource.
	ts, err := auth.NewDefaultTokenSource(false, auth.ScopeUserinfoEmail, auth.ScopeGerrit)
	if err != nil {
		sklog.Fatal(err)
	}
	// Authenticated HTTP client.
	httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()

	// Gitiles repo.
	repo := gitiles.NewRepo(*configRepo, httpClient)

	// Apply configurations in a loop.  Note that we could respond directly to
	// commits in the repo via GitStore and PubSub, but that would require
	// access to BigTable, and with a relatively small interval we won't notice
	// too much of a delay.
	liveness := metrics2.NewLiveness(livenessMetric)
	go util.RepeatCtx(ctx, *interval, func(ctx context.Context) {
		if err := applyConfigs(ctx, repo, *cluster, *configSubdir); err != nil {
			sklog.Errorf("Failed to apply configs to cluster: %s", err)
		} else {
			liveness.Reset()
		}
	})

	select {}
}

func applyConfigs(ctx context.Context, repo *gitiles.Repo, cluster, configSubdir string) error {
	// Download the configs from Gitiles instead of maintaining a local Git
	// checkout, to avoid dealing with Git, persistent checkouts, etc.

	// Obtain the current set of configurations for the cluster.
	head, err := repo.Details(ctx, "main")
	if err != nil {
		return skerr.Wrapf(err, "failed to get most recent commit")
	}
	files, err := repo.ListFilesRecursiveAtRef(ctx, configSubdir, head.Hash)
	if err != nil {
		return skerr.Wrapf(err, "failed to list configs")
	}

	// Read the config contents in the given directory.
	eg := util.NewNamedErrGroup()
	contents := make(map[string][]byte, len(files))
	for _, file := range files {
		eg.Go(file, func() error {
			fullPath := path.Join(configSubdir, file)
			fileContents, err := repo.ReadFileAtRef(ctx, fullPath, head.Hash)
			if err != nil {
				return skerr.Wrapf(err, "failed to retrieve contents of %s", fullPath)
			}
			contents[file] = fileContents
			return nil
		})
	}
	if err := eg.Wait(); err != nil {
		return skerr.Wrapf(err, "failed to download configs")
	}

	// Write the config contents to a temporary dir.
	tmp, err := ioutil.TempDir("", "")
	if err != nil {
		return skerr.Wrapf(err, "failed to create temp dir")
	}
	defer util.RemoveAll(tmp)

	for path, fileContents := range contents {
		fullPath := filepath.Join(tmp, path)
		dir := filepath.Dir(fullPath)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			if err := os.MkdirAll(dir, os.ModePerm); err != nil {
				return skerr.Wrapf(err, "failed to create %s", dir)
			}
		}
		if err := ioutil.WriteFile(fullPath, fileContents, os.ModePerm); err != nil {
			return skerr.Wrapf(err, "failed to create %s", fullPath)
		}
	}

	// Apply the configs to the cluster.
	// Note: this is a very naive approach.  We rely on the Kubernetes server to
	// determine when changes actually need to be made.  We could instead use
	// the REST API to load the full set of configuration which is actually
	// running on the server, diff that against the checked-in config files,
	// and then explicitly make only the changes we want to make.  That would be
	// a much more complicated and error-prone approach, but it would allow us
	// to partially apply configurations in the case of a failure and to alert
	// on specific components which fail to apply for whatever reason.
	output, err := exec.RunCwd(ctx, tmp, "kubectl", "apply", "--prune", "-f", ".")
	if err != nil {
		return skerr.Wrapf(err, "failed to apply configs: %s", output)
	}
	return nil
}
<commit_msg>[BCID] Add health check server to k8s-deployer<commit_after>package main

import (
	"context"
	"flag"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"time"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/metrics2"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
)

const (
	livenessMetric       = "k8s_deployer"
	gitstoreSubscriberID = "k8s-deployer"
)

func main() {
	cluster := flag.String("cluster", "main", "GKE cluster name")
	configRepo := flag.String("config_repo", "https://skia.googlesource.com/k8s-config.git", "Repo containing Kubernetes configurations.")
	configSubdir := flag.String("config_subdir", "", "Subdirectory within the config repo to apply to this cluster.")
	interval := flag.Duration("interval", 10*time.Minute, "How often to re-apply configurations to the cluster")
	port := flag.String("port", ":8000", "HTTP service port for the web server (e.g., ':8000')")
	promPort := flag.String("prom_port", ":20000", "Metrics service address (e.g., ':20000')")

	common.InitWithMust("k8s_deployer", common.PrometheusOpt(promPort))
	defer sklog.Flush()

	if *cluster == "" {
		sklog.Fatal("cluster is required.")
	}
	if *configRepo == "" {
		sklog.Fatal("config_repo is required.")
	}
	if *configSubdir == "" {
		// Note: this wouldn't be required if we had separate config repos per
		// cluster.
		sklog.Fatal("config_subdir is required.")
	}

	ctx := context.Background()

	// OAuth2.0 TokenSource.
	ts, err := auth.NewDefaultTokenSource(false, auth.ScopeUserinfoEmail, auth.ScopeGerrit)
	if err != nil {
		sklog.Fatal(err)
	}
	// Authenticated HTTP client.
	httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()

	// Gitiles repo.
	repo := gitiles.NewRepo(*configRepo, httpClient)

	// Apply configurations in a loop.  Note that we could respond directly to
	// commits in the repo via GitStore and PubSub, but that would require
	// access to BigTable, and with a relatively small interval we won't notice
	// too much of a delay.
	liveness := metrics2.NewLiveness(livenessMetric)
	go util.RepeatCtx(ctx, *interval, func(ctx context.Context) {
		if err := applyConfigs(ctx, repo, *cluster, *configSubdir); err != nil {
			sklog.Errorf("Failed to apply configs to cluster: %s", err)
		} else {
			liveness.Reset()
		}
	})

	// Run health check server.
	httputils.RunHealthCheckServer(*port)
}

func applyConfigs(ctx context.Context, repo *gitiles.Repo, cluster, configSubdir string) error {
	// Download the configs from Gitiles instead of maintaining a local Git
	// checkout, to avoid dealing with Git, persistent checkouts, etc.

	// Obtain the current set of configurations for the cluster.
	head, err := repo.Details(ctx, "main")
	if err != nil {
		return skerr.Wrapf(err, "failed to get most recent commit")
	}
	files, err := repo.ListFilesRecursiveAtRef(ctx, configSubdir, head.Hash)
	if err != nil {
		return skerr.Wrapf(err, "failed to list configs")
	}

	// Read the config contents in the given directory.
	eg := util.NewNamedErrGroup()
	contents := make(map[string][]byte, len(files))
	for _, file := range files {
		eg.Go(file, func() error {
			fullPath := path.Join(configSubdir, file)
			fileContents, err := repo.ReadFileAtRef(ctx, fullPath, head.Hash)
			if err != nil {
				return skerr.Wrapf(err, "failed to retrieve contents of %s", fullPath)
			}
			contents[file] = fileContents
			return nil
		})
	}
	if err := eg.Wait(); err != nil {
		return skerr.Wrapf(err, "failed to download configs")
	}

	// Write the config contents to a temporary dir.
	tmp, err := ioutil.TempDir("", "")
	if err != nil {
		return skerr.Wrapf(err, "failed to create temp dir")
	}
	defer util.RemoveAll(tmp)

	for path, fileContents := range contents {
		fullPath := filepath.Join(tmp, path)
		dir := filepath.Dir(fullPath)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			if err := os.MkdirAll(dir, os.ModePerm); err != nil {
				return skerr.Wrapf(err, "failed to create %s", dir)
			}
		}
		if err := ioutil.WriteFile(fullPath, fileContents, os.ModePerm); err != nil {
			return skerr.Wrapf(err, "failed to create %s", fullPath)
		}
	}

	// Apply the configs to the cluster.
	// Note: this is a very naive approach.  We rely on the Kubernetes server to
	// determine when changes actually need to be made.  We could instead use
	// the REST API to load the full set of configuration which is actually
	// running on the server, diff that against the checked-in config files,
	// and then explicitly make only the changes we want to make.  That would be
	// a much more complicated and error-prone approach, but it would allow us
	// to partially apply configurations in the case of a failure and to alert
	// on specific components which fail to apply for whatever reason.
	output, err := exec.RunCwd(ctx, tmp, "kubectl", "apply", "--prune", "-f", ".")
	if err != nil {
		return skerr.Wrapf(err, "failed to apply configs: %s", output)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
)

func main() {
	elog := log.New(os.Stderr, "ps: ", 0)
	f, err := os.Open("/proc")
	if err != nil {
		elog.Fatal(err)
	}
	pids, err := f.Readdirnames(0)
	if err != nil {
		elog.Fatal(err)
	}
	fmt.Fprintf(os.Stderr, "%5s %6s %s %s\n", "PID", "RSS", "STATE", "CMD")
	for _, pid := range pids {
		if _, err := strconv.Atoi(pid); err != nil {
			continue
		}
		stat, err := ioutil.ReadFile("/proc/" + pid + "/stat")
		if err != nil {
			elog.Print(err)
			continue
		}
		fields := strings.Fields(string(stat))
		name := strings.Trim(fields[1], "()")
		state := fields[2]
		rss := fields[23]
		fmt.Printf("%5s %6s %s %s\n", pid, rss, state, name)
	}
}
<commit_msg>cmd/ps: use tabwriter to pad columns<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"
)

func main() {
	elog := log.New(os.Stderr, "ps: ", 0)
	f, err := os.Open("/proc")
	if err != nil {
		elog.Fatal(err)
	}
	pids, err := f.Readdirnames(0)
	if err != nil {
		elog.Fatal(err)
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
	fmt.Fprintln(w, "PID\tRSS\tSTATE\tCMD")
	defer w.Flush()
	for _, pid := range pids {
		if _, err := strconv.Atoi(pid); err != nil {
			continue
		}
		stat, err := ioutil.ReadFile("/proc/" + pid + "/stat")
		if err != nil {
			elog.Print(err)
			continue
		}
		fields := strings.Fields(string(stat))
		name := strings.Trim(fields[1], "()")
		state := fields[2]
		rss := fields[23]
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", pid, rss, state, name)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"path"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/go-openapi/strfmt"
	"gitlab-odx.oracle.com/odx/functions/api"
	"gitlab-odx.oracle.com/odx/functions/api/id"
	"gitlab-odx.oracle.com/odx/functions/api/models"
	"gitlab-odx.oracle.com/odx/functions/api/runner"
	"gitlab-odx.oracle.com/odx/functions/api/runner/common"
	"gitlab-odx.oracle.com/odx/functions/api/runner/task"
)

type runnerResponse struct {
	RequestID string            `json:"request_id,omitempty"`
	Error     *models.ErrorBody `json:"error,omitempty"`
}

func (s *Server) handleSpecial(c *gin.Context) {
	ctx := c.MustGet("ctx").(context.Context)
	log := common.Logger(ctx)

	ctx = context.WithValue(ctx, api.AppName, "")
	c.Set(api.AppName, "")
	ctx = context.WithValue(ctx, api.Path, c.Request.URL.Path)
	c.Set(api.Path, c.Request.URL.Path)

	ctx, err := s.UseSpecialHandlers(ctx, c.Request, c.Writer)
	if err == ErrNoSpecialHandlerFound {
		log.WithError(err).Errorln("Not special handler found")
		c.JSON(http.StatusNotFound, http.StatusText(http.StatusNotFound))
		return
	} else if err != nil {
		log.WithError(err).Errorln("Error using special handler!")
		c.JSON(http.StatusInternalServerError, simpleError(errors.New("Failed to run function")))
		return
	}

	c.Set("ctx", ctx)
	c.Set(api.AppName, ctx.Value(api.AppName).(string))
	if c.MustGet(api.AppName).(string) == "" {
		log.WithError(err).Errorln("Specialhandler returned empty app name")
		c.JSON(http.StatusBadRequest, simpleError(models.ErrRunnerRouteNotFound))
		return
	}

	// now call the normal runner call
	s.handleRequest(c, nil)
}

func toEnvName(envtype, name string) string {
	name = strings.ToUpper(strings.Replace(name, "-", "_", -1))
	if envtype == "" {
		return name
	}
	return fmt.Sprintf("%s_%s", envtype, name)
}

func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) {
	if strings.HasPrefix(c.Request.URL.Path, "/v1") {
		c.Status(http.StatusNotFound)
		return
	}

	ctx := c.MustGet("ctx").(context.Context)

	reqID := id.New().String()
	ctx, log := common.LoggerWithFields(ctx, logrus.Fields{"call_id": reqID})

	var err error
	var payload io.Reader

	if c.Request.Method == "POST" {
		payload = c.Request.Body
		// Load complete body and close
		defer func() {
			io.Copy(ioutil.Discard, c.Request.Body)
			c.Request.Body.Close()
		}()
	} else if c.Request.Method == "GET" {
		reqPayload := c.Request.URL.Query().Get("payload")
		payload = strings.NewReader(reqPayload)
	}

	reqRoute := &models.Route{
		AppName: c.MustGet(api.AppName).(string),
		Path:    path.Clean(c.MustGet(api.Path).(string)),
	}

	s.FireBeforeDispatch(ctx, reqRoute)

	appName := reqRoute.AppName
	path := reqRoute.Path

	app, err := s.Datastore.GetApp(ctx, appName)
	if err != nil || app == nil {
		log.WithError(err).Error(models.ErrAppsNotFound)
		c.JSON(http.StatusNotFound, simpleError(models.ErrAppsNotFound))
		return
	}

	log.WithFields(logrus.Fields{"app": appName, "path": path}).Debug("Finding route on datastore")
	routes, err := s.loadroutes(ctx, models.RouteFilter{AppName: appName, Path: path})
	if err != nil {
		log.WithError(err).Error(models.ErrRoutesList)
		c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesList))
		return
	}

	if len(routes) == 0 {
		log.WithError(err).Error(models.ErrRunnerRouteNotFound)
		c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
		return
	}

	log.WithField("routes", len(routes)).Debug("Got routes from datastore")
	route := routes[0]
	log = log.WithFields(logrus.Fields{"app": appName, "path": route.Path, "image": route.Image})

	if s.serve(ctx, c, appName, route, app, path, reqID, payload, enqueue) {
		s.FireAfterDispatch(ctx, reqRoute)
		return
	}

	log.Error(models.ErrRunnerRouteNotFound)
	c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
}

func (s *Server) loadroutes(ctx context.Context, filter models.RouteFilter) ([]*models.Route, error) {
	if route, ok := s.cacheget(filter.AppName, filter.Path); ok {
		return []*models.Route{route}, nil
	}
	resp, err := s.singleflight.do(
		filter,
		func() (interface{}, error) {
			return s.Datastore.GetRoutesByApp(ctx, filter.AppName, &filter)
		},
	)
	return resp.([]*models.Route), err
}

// TODO: Should remove *gin.Context from these functions, should use only context.Context
func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, found *models.Route, app *models.App, route, reqID string, payload io.Reader, enqueue models.Enqueue) (ok bool) {
	ctx, log := common.LoggerWithFields(ctx, logrus.Fields{"app": appName, "route": found.Path, "image": found.Image})

	params, match := matchRoute(found.Path, route)
	if !match {
		return false
	}

	var stdout bytes.Buffer // TODO: should limit the size of this, error if gets too big. akin to: https://golang.org/pkg/io/#LimitReader

	envVars := map[string]string{
		"METHOD":   c.Request.Method,
		"APP_NAME": appName,
		"ROUTE":    found.Path,
		"REQUEST_URL": fmt.Sprintf("%v//%v%v", func() string {
			if c.Request.TLS == nil {
				return "http"
			}
			return "https"
		}(), c.Request.Host, c.Request.URL.String()),
	}

	// app config
	for k, v := range app.Config {
		envVars[toEnvName("", k)] = v
	}
	for k, v := range found.Config {
		envVars[toEnvName("", k)] = v
	}

	// params
	for _, param := range params {
		envVars[toEnvName("PARAM", param.Key)] = param.Value
	}

	// headers
	for header, value := range c.Request.Header {
		envVars[toEnvName("HEADER", header)] = strings.Join(value, " ")
	}

	cfg := &task.Config{
		AppName:      appName,
		Path:         found.Path,
		Env:          envVars,
		Format:       found.Format,
		ID:           reqID,
		Image:        found.Image,
		Memory:       found.Memory,
		Stdin:        payload,
		Stdout:       &stdout,
		Timeout:      time.Duration(found.Timeout) * time.Second,
		IdleTimeout:  time.Duration(found.IdleTimeout) * time.Second,
		ReceivedTime: time.Now(),
		Ready:        make(chan struct{}),
	}

	// ensure valid values
	if cfg.Timeout <= 0 {
		cfg.Timeout = runner.DefaultTimeout
	}
	if cfg.IdleTimeout <= 0 {
		cfg.IdleTimeout = runner.DefaultIdleTimeout
	}

	s.Runner.Enqueue()
	createdAt := strfmt.DateTime(time.Now())
	newTask := &models.Task{}
	newTask.Image = &cfg.Image
	newTask.ID = cfg.ID
	newTask.CreatedAt = createdAt
	newTask.Path = found.Path
	newTask.EnvVars = cfg.Env
	newTask.AppName = cfg.AppName

	switch found.Type {
	case "async":
		// Read payload
		pl, err := ioutil.ReadAll(cfg.Stdin)
		if err != nil {
			log.WithError(err).Error(models.ErrInvalidPayload)
			c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidPayload))
			return true
		}
		// Create Task
		priority := int32(0)
		newTask.Priority = &priority
		newTask.Payload = string(pl)
		// Push to queue
		enqueue(c, s.MQ, newTask)
		log.Info("Added new task to queue")
		c.JSON(http.StatusAccepted, map[string]string{"call_id": newTask.ID})

	default:
		result, err := s.Runner.RunTrackedTask(newTask, ctx, cfg)
		if result != nil {
			waitTime := result.StartTime().Sub(cfg.ReceivedTime)
			c.Header("XXX-FXLB-WAIT", waitTime.String())
		}

		if err != nil {
			c.JSON(http.StatusInternalServerError, runnerResponse{
				RequestID: cfg.ID,
				Error: &models.ErrorBody{
					Message: err.Error(),
				},
			})
			log.WithError(err).Error("Failed to run task")
			break
		}

		for k, v := range found.Headers {
			c.Header(k, v[0])
		}

		// this will help users to track sync execution in a manner of async
		// FN_CALL_ID is an equivalent of call_id
		c.Header("FN_CALL_ID", newTask.ID)

		switch result.Status() {
		case "success":
			c.Data(http.StatusOK, "", stdout.Bytes())
		case "timeout":
			c.JSON(http.StatusGatewayTimeout, runnerResponse{
				RequestID: cfg.ID,
				Error: &models.ErrorBody{
					Message: models.ErrRunnerTimeout.Error(),
				},
			})
		default:
			c.JSON(http.StatusInternalServerError, runnerResponse{
				RequestID: cfg.ID,
				Error: &models.ErrorBody{
					Message: result.Error(),
				},
			})
		}
	}

	return true
}

var fakeHandler = func(http.ResponseWriter, *http.Request, Params) {}

func matchRoute(baseRoute, route string) (Params, bool) {
	tree := &node{}
	tree.addRoute(baseRoute, fakeHandler)
	handler, p, _ := tree.getValue(route)
	if handler == nil {
		return nil, false
	}

	return p, true
}
<commit_msg>Pass call ID into function, closes #79<commit_after>package server

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"path"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/go-openapi/strfmt"
	"gitlab-odx.oracle.com/odx/functions/api"
	"gitlab-odx.oracle.com/odx/functions/api/id"
	"gitlab-odx.oracle.com/odx/functions/api/models"
	"gitlab-odx.oracle.com/odx/functions/api/runner"
	"gitlab-odx.oracle.com/odx/functions/api/runner/common"
	"gitlab-odx.oracle.com/odx/functions/api/runner/task"
)

type runnerResponse struct {
	RequestID string            `json:"request_id,omitempty"`
	Error     *models.ErrorBody `json:"error,omitempty"`
}

func (s *Server) handleSpecial(c *gin.Context) {
	ctx := c.MustGet("ctx").(context.Context)
	log := common.Logger(ctx)

	ctx = context.WithValue(ctx, api.AppName, "")
	c.Set(api.AppName, "")
	ctx = context.WithValue(ctx, api.Path, c.Request.URL.Path)
	c.Set(api.Path, c.Request.URL.Path)

	ctx, err := s.UseSpecialHandlers(ctx, c.Request, c.Writer)
	if err == ErrNoSpecialHandlerFound {
		log.WithError(err).Errorln("Not special handler found")
		c.JSON(http.StatusNotFound, http.StatusText(http.StatusNotFound))
		return
	} else if err != nil {
		log.WithError(err).Errorln("Error using special handler!")
		c.JSON(http.StatusInternalServerError, simpleError(errors.New("Failed to run function")))
		return
	}

	c.Set("ctx", ctx)
	c.Set(api.AppName, ctx.Value(api.AppName).(string))
	if c.MustGet(api.AppName).(string) == "" {
		log.WithError(err).Errorln("Specialhandler returned empty app name")
		c.JSON(http.StatusBadRequest, simpleError(models.ErrRunnerRouteNotFound))
		return
	}

	// now call the normal runner call
	s.handleRequest(c, nil)
}

func toEnvName(envtype, name string) string {
	name = strings.ToUpper(strings.Replace(name, "-", "_", -1))
	if envtype == "" {
		return name
	}
	return fmt.Sprintf("%s_%s", envtype, name)
}

func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) {
	if strings.HasPrefix(c.Request.URL.Path, "/v1") {
		c.Status(http.StatusNotFound)
		return
	}

	ctx := c.MustGet("ctx").(context.Context)

	reqID := id.New().String()
	ctx, log := common.LoggerWithFields(ctx, logrus.Fields{"call_id": reqID})

	var err error
	var payload io.Reader

	if c.Request.Method == "POST" {
		payload = c.Request.Body
		// Load complete body and close
		defer func() {
			io.Copy(ioutil.Discard, c.Request.Body)
			c.Request.Body.Close()
		}()
	} else if c.Request.Method == "GET" {
		reqPayload := c.Request.URL.Query().Get("payload")
		payload = strings.NewReader(reqPayload)
	}

	reqRoute := &models.Route{
		AppName: c.MustGet(api.AppName).(string),
		Path:    path.Clean(c.MustGet(api.Path).(string)),
	}

	s.FireBeforeDispatch(ctx, reqRoute)

	appName := reqRoute.AppName
	path := reqRoute.Path

	app, err := s.Datastore.GetApp(ctx, appName)
	if err != nil || app == nil {
		log.WithError(err).Error(models.ErrAppsNotFound)
		c.JSON(http.StatusNotFound, simpleError(models.ErrAppsNotFound))
		return
	}

	log.WithFields(logrus.Fields{"app": appName, "path": path}).Debug("Finding route on datastore")
	routes, err := s.loadroutes(ctx, models.RouteFilter{AppName: appName, Path: path})
	if err != nil {
		log.WithError(err).Error(models.ErrRoutesList)
		c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesList))
		return
	}

	if len(routes) == 0 {
		log.WithError(err).Error(models.ErrRunnerRouteNotFound)
		c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
		return
	}

	log.WithField("routes", len(routes)).Debug("Got routes from datastore")
	route := routes[0]
	log = log.WithFields(logrus.Fields{"app": appName, "path": route.Path, "image": route.Image})

	if s.serve(ctx, c, appName, route, app, path, reqID, payload, enqueue) {
		s.FireAfterDispatch(ctx, reqRoute)
		return
	}

	log.Error(models.ErrRunnerRouteNotFound)
	c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
}

func (s *Server) loadroutes(ctx context.Context, filter models.RouteFilter) ([]*models.Route, error) {
	if route, ok := s.cacheget(filter.AppName, filter.Path); ok {
		return []*models.Route{route}, nil
	}
	resp, err := s.singleflight.do(
		filter,
		func() (interface{}, error) {
			return s.Datastore.GetRoutesByApp(ctx, filter.AppName, &filter)
		},
	)
	return resp.([]*models.Route), err
}

// TODO: Should remove *gin.Context from these functions, should use only context.Context
func (s *Server) serve(ctx context.Context, c *gin.Context, appName string, found *models.Route, app *models.App, route, reqID string, payload io.Reader, enqueue models.Enqueue) (ok bool) {
	ctx, log := common.LoggerWithFields(ctx, logrus.Fields{"app": appName, "route": found.Path, "image": found.Image})

	params, match := matchRoute(found.Path, route)
	if !match {
		return false
	}

	var stdout bytes.Buffer // TODO: should limit the size of this, error if gets too big. akin to: https://golang.org/pkg/io/#LimitReader

	envVars := map[string]string{
		"METHOD":   c.Request.Method,
		"APP_NAME": appName,
		"ROUTE":    found.Path,
		"REQUEST_URL": fmt.Sprintf("%v//%v%v", func() string {
			if c.Request.TLS == nil {
				return "http"
			}
			return "https"
		}(), c.Request.Host, c.Request.URL.String()),
		"CALL_ID": reqID,
	}

	// app config
	for k, v := range app.Config {
		envVars[toEnvName("", k)] = v
	}
	for k, v := range found.Config {
		envVars[toEnvName("", k)] = v
	}

	// params
	for _, param := range params {
		envVars[toEnvName("PARAM", param.Key)] = param.Value
	}

	// headers
	for header, value := range c.Request.Header {
		envVars[toEnvName("HEADER", header)] = strings.Join(value, " ")
	}

	cfg := &task.Config{
		AppName:      appName,
		Path:         found.Path,
		Env:          envVars,
		Format:       found.Format,
		ID:           reqID,
		Image:        found.Image,
		Memory:       found.Memory,
		Stdin:        payload,
		Stdout:       &stdout,
		Timeout:      time.Duration(found.Timeout) * time.Second,
		IdleTimeout:  time.Duration(found.IdleTimeout) * time.Second,
		ReceivedTime: time.Now(),
		Ready:        make(chan struct{}),
	}

	// ensure valid values
	if cfg.Timeout <= 0 {
		cfg.Timeout = runner.DefaultTimeout
	}
	if cfg.IdleTimeout <= 0 {
		cfg.IdleTimeout = runner.DefaultIdleTimeout
	}

	s.Runner.Enqueue()
	createdAt := strfmt.DateTime(time.Now())
	newTask := &models.Task{}
	newTask.Image = &cfg.Image
	newTask.ID = cfg.ID
	newTask.CreatedAt = createdAt
	newTask.Path = found.Path
	newTask.EnvVars = cfg.Env
	newTask.AppName = cfg.AppName

	switch found.Type {
	case "async":
		// Read payload
		pl, err := ioutil.ReadAll(cfg.Stdin)
		if err != nil {
			log.WithError(err).Error(models.ErrInvalidPayload)
			c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidPayload))
			return true
		}
		// Create Task
		priority := int32(0)
		newTask.Priority = &priority
		newTask.Payload = string(pl)
		// Push to queue
		enqueue(c, s.MQ, newTask)
		log.Info("Added new task to queue")
		c.JSON(http.StatusAccepted, map[string]string{"call_id": newTask.ID})

	default:
		result, err := s.Runner.RunTrackedTask(newTask, ctx, cfg)
		if result != nil {
			waitTime := result.StartTime().Sub(cfg.ReceivedTime)
			c.Header("XXX-FXLB-WAIT", waitTime.String())
		}

		if err != nil {
			c.JSON(http.StatusInternalServerError, runnerResponse{
				RequestID: cfg.ID,
				Error: &models.ErrorBody{
					Message: err.Error(),
				},
			})
			log.WithError(err).Error("Failed to run task")
			break
		}

		for k, v := range found.Headers {
			c.Header(k, v[0])
		}

		// this will help users to track sync execution in a manner of async
		// FN_CALL_ID is an equivalent of call_id
		c.Header("FN_CALL_ID", newTask.ID)

		switch result.Status() {
		case "success":
			c.Data(http.StatusOK, "", stdout.Bytes())
		case "timeout":
			c.JSON(http.StatusGatewayTimeout, runnerResponse{
				RequestID: cfg.ID,
				Error: &models.ErrorBody{
					Message: models.ErrRunnerTimeout.Error(),
				},
			})
		default:
			c.JSON(http.StatusInternalServerError, runnerResponse{
				RequestID: cfg.ID,
				Error: &models.ErrorBody{
					Message: result.Error(),
				},
			})
		}
	}

	return true
}

var fakeHandler = func(http.ResponseWriter, *http.Request, Params) {}

func matchRoute(baseRoute, route string) (Params, bool) {
	tree := &node{}
	tree.addRoute(baseRoute, fakeHandler)
	handler, p, _ := tree.getValue(route)
	if handler == nil {
		return nil, false
	}

	return p, true
}
<|endoftext|>


<commit_before>package eventemitter

import (
	"reflect"
)

type Event struct {
	Name   string
	Argv   []interface{}
	Result interface{}
}

type EventEmitter struct {
	Events map[string][]reflect.Value
}

func NewEventEmitter() *EventEmitter {
	e := new(EventEmitter)
	e.Init()

	return e
}

// Allocates the EventEmitters memory. Has to be called when
// embedding an EventEmitter in another Type.
func (self *EventEmitter) Init() {
	self.Events = make(map[string][]reflect.Value)
}

// Alias to AddListener.
func (self *EventEmitter) On(event string, listener interface{}) {
	self.AddListener(event, listener)
}

// AddListener adds an event listener on the given event name.
func (self *EventEmitter) AddListener(event string, listener interface{}) {
	// Check if the event exists, otherwise initialize the list
	// of handlers for this event.
	if _, exists := self.Events[event]; !exists {
		self.Events[event] = []reflect.Value{}
	}

	if l, ok := listener.(reflect.Value); ok {
		self.Events[event] = append(self.Events[event], l)
	} else {
		l := reflect.ValueOf(listener)
		self.Events[event] = append(self.Events[event], l)
	}
}

// Removes all listeners from the given event.
func (self *EventEmitter) RemoveListeners(event string) {
	delete(self.Events, event)
}

// Emits the given event. Puts all arguments following the event name
// into the Event's `Argv` member. Returns a channel if listeners were
// called, nil otherwise.
func (self *EventEmitter) Emit(event string, argv ...interface{}) <-chan []interface{} {
	listeners, exists := self.Events[event]

	if !exists {
		return nil
	}

	var callArgv []reflect.Value
	c := make(chan []interface{})

	for _, a := range argv {
		callArgv = append(callArgv, reflect.ValueOf(a))
	}

	for _, listener := range listeners {
		go func() {
			retVals := listener.Call(callArgv)
			response := []interface{}{}

			for _, r := range retVals {
				response = append(response, r.Interface())
			}

			c <- response
		}()
	}

	return c
}
<commit_msg>Using var for making response slice.<commit_after>package eventemitter

import (
	"reflect"
)

type Event struct {
	Name   string
	Argv   []interface{}
	Result interface{}
}

type EventEmitter struct {
	Events map[string][]reflect.Value
}

func NewEventEmitter() *EventEmitter {
	e := new(EventEmitter)
	e.Init()

	return e
}

// Allocates the EventEmitters memory. Has to be called when
// embedding an EventEmitter in another Type.
func (self *EventEmitter) Init() {
	self.Events = make(map[string][]reflect.Value)
}

// Alias to AddListener.
func (self *EventEmitter) On(event string, listener interface{}) {
	self.AddListener(event, listener)
}

// AddListener adds an event listener on the given event name.
func (self *EventEmitter) AddListener(event string, listener interface{}) {
	// Check if the event exists, otherwise initialize the list
	// of handlers for this event.
	if _, exists := self.Events[event]; !exists {
		self.Events[event] = []reflect.Value{}
	}

	if l, ok := listener.(reflect.Value); ok {
		self.Events[event] = append(self.Events[event], l)
	} else {
		l := reflect.ValueOf(listener)
		self.Events[event] = append(self.Events[event], l)
	}
}

// Removes all listeners from the given event.
func (self *EventEmitter) RemoveListeners(event string) {
	delete(self.Events, event)
}

// Emits the given event. Puts all arguments following the event name
// into the Event's `Argv` member. Returns a channel if listeners were
// called, nil otherwise.
func (self *EventEmitter) Emit(event string, argv ...interface{}) <-chan []interface{} {
	listeners, exists := self.Events[event]

	if !exists {
		return nil
	}

	var callArgv []reflect.Value
	c := make(chan []interface{})

	for _, a := range argv {
		callArgv = append(callArgv, reflect.ValueOf(a))
	}

	for _, listener := range listeners {
		go func() {
			retVals := listener.Call(callArgv)
			var response []interface{}

			for _, r := range retVals {
				response = append(response, r.Interface())
			}

			c <- response
		}()
	}

	return c
}
<|endoftext|>