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

package rest

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/gorilla/mux"

	"github.com/couchbaselabs/sync_gateway/auth"
	"github.com/couchbaselabs/sync_gateway/base"
)

//////// USER & ROLE REQUESTS:

// Common behavior of putUser and putRole
func putPrincipal(r http.ResponseWriter, rq *http.Request, context *context, name string, princ auth.Principal) error {
	context.auth.InvalidateChannels(princ)

	if rq.Method == "POST" {
		name = princ.Name()
		if name == "" {
			return &base.HTTPError{http.StatusBadRequest, "Missing name property"}
		}
	} else if princ.Name() != name {
		return &base.HTTPError{http.StatusBadRequest, "Name mismatch (can't change name)"}
	}
	err := context.auth.Save(princ)
	if err == nil {
		r.WriteHeader(http.StatusCreated)
	}
	return err
}

// Handles PUT or POST to /user/*
func putUser(r http.ResponseWriter, rq *http.Request, context *context) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	body, _ := ioutil.ReadAll(rq.Body)
	user, err := context.auth.UnmarshalUser(body, username, context.dbcontext.LastSequence()+1)
	if err != nil {
		return err
	}
	return putPrincipal(r, rq, context, username, user)
}

// Handles PUT or POST to /role/*
func putRole(r http.ResponseWriter, rq *http.Request, context *context) error {
	rolename := mux.Vars(rq)["name"]
	body, _ := ioutil.ReadAll(rq.Body)
	role, err := context.auth.UnmarshalRole(body, rolename, context.dbcontext.LastSequence()+1)
	if err != nil {
		return err
	}
	return putPrincipal(r, rq, context, rolename, role)
}

func deleteUser(r http.ResponseWriter, rq *http.Request, context *context) error {
	user, err := context.auth.GetUser(mux.Vars(rq)["name"])
	if user == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	return context.auth.Delete(user)
}

func deleteRole(r http.ResponseWriter, rq *http.Request, context *context) error {
	role, err := context.auth.GetRole(mux.Vars(rq)["name"])
	if role == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	return context.auth.Delete(role)
}

func getUserInfo(r http.ResponseWriter, rq *http.Request, context *context) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	user, err := context.auth.GetUser(username)
	if user == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	bytes, err := json.Marshal(user)
	r.Write(bytes)
	return err
}

func getRoleInfo(r http.ResponseWriter, rq *http.Request, context *context) error {
	role, err := context.auth.GetRole(mux.Vars(rq)["name"])
	if role == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	bytes, err := json.Marshal(role)
	r.Write(bytes)
	return err
}

func getUsers(r http.ResponseWriter, rq *http.Request, context *context) error {
	users, _, err := context.dbcontext.AllPrincipalIDs()
	if err != nil {
		return err
	}
	bytes, err := json.Marshal(users)
	r.Write(bytes)
	return err
}

func getRoles(r http.ResponseWriter, rq *http.Request, context *context) error {
	_, roles, err := context.dbcontext.AllPrincipalIDs()
	if err != nil {
		return err
	}
	bytes, err := json.Marshal(roles)
	r.Write(bytes)
	return err
}

//////// SESSION:

// Generates a login session for a user and returns the session ID and cookie name.
func createUserSession(r http.ResponseWriter, rq *http.Request, context *context) error {
	body, err := ioutil.ReadAll(rq.Body)
	if err != nil {
		return err
	}
	var params struct {
		Name string        `json:"name"`
		TTL  time.Duration `json:"ttl"`
	}
	err = json.Unmarshal(body, &params)
	if err != nil {
		return err
	}
	if params.Name == "" || params.TTL < 0 {
		return &base.HTTPError{http.StatusBadRequest, "Invalid name or ttl"}
	}
	session, err := context.auth.CreateSession(params.Name, params.TTL)
	if err != nil {
		return err
	}
	var response struct {
		SessionID  string    `json:"session_id"`
		Expires    time.Time `json:"expires"`
		CookieName string    `json:"cookie_name"`
	}
	response.SessionID = session.ID
	response.Expires = session.Expiration
	response.CookieName = auth.CookieName
	bytes, _ := json.Marshal(response)
	r.Header().Set("Content-Type", "application/json")
	r.Write(bytes)
	return nil
}

//////// HTTP HANDLER:

func renderError(err error, r http.ResponseWriter) {
	status, message := base.ErrorAsHTTPStatus(err)
	r.Header().Set("Content-Type", "application/json")
	r.WriteHeader(status)
	jsonOut, _ := json.Marshal(map[string]interface{}{"error": status, "reason": message})
	r.Write(jsonOut)
}

type authHandler func(http.ResponseWriter, *http.Request, *context) error

func handleAuthReq(sc *serverContext, fun authHandler) func(http.ResponseWriter, *http.Request) {
	return func(r http.ResponseWriter, rq *http.Request) {
		dbContext := sc.databases[mux.Vars(rq)["db"]]
		if dbContext == nil {
			r.WriteHeader(http.StatusNotFound)
			return
		}
		err := fun(r, rq, dbContext)
		if err != nil {
			renderError(err, r)
		}
	}
}

// Starts a simple REST listener that will get and set user credentials.
func createAuthHandler(sc *serverContext) http.Handler {
	r := mux.NewRouter()
	r.StrictSlash(true)

	r.HandleFunc("/{db}/_session",
		handleAuthReq(sc, createUserSession)).Methods("POST")

	r.HandleFunc("/{db}/user",
		handleAuthReq(sc, getUsers)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(sc, getUserInfo)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(sc, putUser)).Methods("PUT")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(sc, deleteUser)).Methods("DELETE")
	r.HandleFunc("/{db}/user/",
		handleAuthReq(sc, putUser)).Methods("POST")

	r.HandleFunc("/{db}/role",
		handleAuthReq(sc, getUsers)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/role/{name}",
		handleAuthReq(sc, getRoleInfo)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/role/{name}",
		handleAuthReq(sc, putRole)).Methods("PUT")
	r.HandleFunc("/{db}/role/{name}",
		handleAuthReq(sc, deleteRole)).Methods("DELETE")
	r.HandleFunc("/{db}/role/",
		handleAuthReq(sc, putRole)).Methods("POST")

	// The routes below are part of the CouchDB REST API but should only be available to admins,
	// so the handlers are moved to the admin port.
	r.Handle("/{db}/", makeAdminHandler(sc, (*handler).handleDeleteDB)).Methods("DELETE")
	dbr := r.PathPrefix("/{db}/").Subrouter()
	dbr.Handle("/_vacuum",
		makeAdminHandler(sc, (*handler).handleVacuum)).Methods("POST")
	dbr.Handle("/_dump/{view}",
		makeAdminHandler(sc, (*handler).handleDump)).Methods("GET")

	// These routes are available on both the regular and admin ports; the admin port is useful
	// because it gives 'superuser' access.
	dbr.Handle("/_all_docs",
		makeAdminHandler(sc, (*handler).handleAllDocs)).Methods("GET", "HEAD", "POST")
	dbr.Handle("/_changes",
		makeAdminHandler(sc, (*handler).handleChanges)).Methods("GET", "HEAD")

	return r
}

func StartAuthListener(addr string, sc *serverContext) {
	go http.ListenAndServe(addr, createAuthHandler(sc))
}
<commit_msg>Oops: Admin GET /db/role was returning list of users, not roles<commit_after>//  Copyright (c) 2012 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package rest

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/gorilla/mux"

	"github.com/couchbaselabs/sync_gateway/auth"
	"github.com/couchbaselabs/sync_gateway/base"
)

//////// USER & ROLE REQUESTS:

// Common behavior of putUser and putRole
func putPrincipal(r http.ResponseWriter, rq *http.Request, context *context, name string, princ auth.Principal) error {
	context.auth.InvalidateChannels(princ)

	if rq.Method == "POST" {
		name = princ.Name()
		if name == "" {
			return &base.HTTPError{http.StatusBadRequest, "Missing name property"}
		}
	} else if princ.Name() != name {
		return &base.HTTPError{http.StatusBadRequest, "Name mismatch (can't change name)"}
	}
	err := context.auth.Save(princ)
	if err == nil {
		r.WriteHeader(http.StatusCreated)
	}
	return err
}

// Handles PUT or POST to /user/*
func putUser(r http.ResponseWriter, rq *http.Request, context *context) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	body, _ := ioutil.ReadAll(rq.Body)
	user, err := context.auth.UnmarshalUser(body, username, context.dbcontext.LastSequence()+1)
	if err != nil {
		return err
	}
	return putPrincipal(r, rq, context, username, user)
}

// Handles PUT or POST to /role/*
func putRole(r http.ResponseWriter, rq *http.Request, context *context) error {
	rolename := mux.Vars(rq)["name"]
	body, _ := ioutil.ReadAll(rq.Body)
	role, err := context.auth.UnmarshalRole(body, rolename, context.dbcontext.LastSequence()+1)
	if err != nil {
		return err
	}
	return putPrincipal(r, rq, context, rolename, role)
}

func deleteUser(r http.ResponseWriter, rq *http.Request, context *context) error {
	user, err := context.auth.GetUser(mux.Vars(rq)["name"])
	if user == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	return context.auth.Delete(user)
}

func deleteRole(r http.ResponseWriter, rq *http.Request, context *context) error {
	role, err := context.auth.GetRole(mux.Vars(rq)["name"])
	if role == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	return context.auth.Delete(role)
}

func getUserInfo(r http.ResponseWriter, rq *http.Request, context *context) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	user, err := context.auth.GetUser(username)
	if user == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	bytes, err := json.Marshal(user)
	r.Write(bytes)
	return err
}

func getRoleInfo(r http.ResponseWriter, rq *http.Request, context *context) error {
	role, err := context.auth.GetRole(mux.Vars(rq)["name"])
	if role == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	bytes, err := json.Marshal(role)
	r.Write(bytes)
	return err
}

func getUsers(r http.ResponseWriter, rq *http.Request, context *context) error {
	users, _, err := context.dbcontext.AllPrincipalIDs()
	if err != nil {
		return err
	}
	bytes, err := json.Marshal(users)
	r.Write(bytes)
	return err
}

func getRoles(r http.ResponseWriter, rq *http.Request, context *context) error {
	_, roles, err := context.dbcontext.AllPrincipalIDs()
	if err != nil {
		return err
	}
	bytes, err := json.Marshal(roles)
	r.Write(bytes)
	return err
}

//////// SESSION:

// Generates a login session for a user and returns the session ID and cookie name.
func createUserSession(r http.ResponseWriter, rq *http.Request, context *context) error {
	body, err := ioutil.ReadAll(rq.Body)
	if err != nil {
		return err
	}
	var params struct {
		Name string        `json:"name"`
		TTL  time.Duration `json:"ttl"`
	}
	err = json.Unmarshal(body, &params)
	if err != nil {
		return err
	}
	if params.Name == "" || params.TTL < 0 {
		return &base.HTTPError{http.StatusBadRequest, "Invalid name or ttl"}
	}
	session, err := context.auth.CreateSession(params.Name, params.TTL)
	if err != nil {
		return err
	}
	var response struct {
		SessionID  string    `json:"session_id"`
		Expires    time.Time `json:"expires"`
		CookieName string    `json:"cookie_name"`
	}
	response.SessionID = session.ID
	response.Expires = session.Expiration
	response.CookieName = auth.CookieName
	bytes, _ := json.Marshal(response)
	r.Header().Set("Content-Type", "application/json")
	r.Write(bytes)
	return nil
}

//////// HTTP HANDLER:

func renderError(err error, r http.ResponseWriter) {
	status, message := base.ErrorAsHTTPStatus(err)
	r.Header().Set("Content-Type", "application/json")
	r.WriteHeader(status)
	jsonOut, _ := json.Marshal(map[string]interface{}{"error": status, "reason": message})
	r.Write(jsonOut)
}

type authHandler func(http.ResponseWriter, *http.Request, *context) error

func handleAuthReq(sc *serverContext, fun authHandler) func(http.ResponseWriter, *http.Request) {
	return func(r http.ResponseWriter, rq *http.Request) {
		dbContext := sc.databases[mux.Vars(rq)["db"]]
		if dbContext == nil {
			r.WriteHeader(http.StatusNotFound)
			return
		}
		err := fun(r, rq, dbContext)
		if err != nil {
			renderError(err, r)
		}
	}
}

// Starts a simple REST listener that will get and set user credentials.
func createAuthHandler(sc *serverContext) http.Handler {
	r := mux.NewRouter()
	r.StrictSlash(true)

	r.HandleFunc("/{db}/_session",
		handleAuthReq(sc, createUserSession)).Methods("POST")

	r.HandleFunc("/{db}/user",
		handleAuthReq(sc, getUsers)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(sc, getUserInfo)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(sc, putUser)).Methods("PUT")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(sc, deleteUser)).Methods("DELETE")
	r.HandleFunc("/{db}/user/",
		handleAuthReq(sc, putUser)).Methods("POST")

	r.HandleFunc("/{db}/role",
		handleAuthReq(sc, getRoles)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/role/{name}",
		handleAuthReq(sc, getRoleInfo)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/role/{name}",
		handleAuthReq(sc, putRole)).Methods("PUT")
	r.HandleFunc("/{db}/role/{name}",
		handleAuthReq(sc, deleteRole)).Methods("DELETE")
	r.HandleFunc("/{db}/role/",
		handleAuthReq(sc, putRole)).Methods("POST")

	// The routes below are part of the CouchDB REST API but should only be available to admins,
	// so the handlers are moved to the admin port.
	r.Handle("/{db}/", makeAdminHandler(sc, (*handler).handleDeleteDB)).Methods("DELETE")
	dbr := r.PathPrefix("/{db}/").Subrouter()
	dbr.Handle("/_vacuum",
		makeAdminHandler(sc, (*handler).handleVacuum)).Methods("POST")
	dbr.Handle("/_dump/{view}",
		makeAdminHandler(sc, (*handler).handleDump)).Methods("GET")

	// These routes are available on both the regular and admin ports; the admin port is useful
	// because it gives 'superuser' access.
	dbr.Handle("/_all_docs",
		makeAdminHandler(sc, (*handler).handleAllDocs)).Methods("GET", "HEAD", "POST")
	dbr.Handle("/_changes",
		makeAdminHandler(sc, (*handler).handleChanges)).Methods("GET", "HEAD")

	return r
}

func StartAuthListener(addr string, sc *serverContext) {
	go http.ListenAndServe(addr, createAuthHandler(sc))
}
<|endoftext|>


<commit_before>package pipeline

import (
	"log"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/eliothedeman/bangarang/alarm"
	"github.com/eliothedeman/bangarang/config"
	"github.com/eliothedeman/bangarang/event"
	"github.com/eliothedeman/bangarang/provider"
)

const (
	KEEP_ALIVE_SERVICE_NAME = "KeepAlive"
)

type Pipeline struct {
	keepAliveAge       time.Duration
	keepAliveCheckTime time.Duration
	globalPolicy       *alarm.Policy
	escalations        *alarm.Collection
	policies           map[string]*alarm.Policy
	index              *event.Index
	providers          provider.EventProviderCollection
	encodingPool       *event.EncodingPool
	config             *config.AppConfig
	tracker            *Tracker
	pauseCache         map[*event.Event]struct{}
	unpauseChan        chan struct{}
	in                 chan *event.Event
}

func NewPipeline(conf *config.AppConfig) *Pipeline {
	p := &Pipeline{
		encodingPool:       event.NewEncodingPool(event.EncoderFactories[conf.Encoding], event.DecoderFactories[conf.Encoding], runtime.NumCPU()),
		keepAliveAge:       conf.KeepAliveAge,
		keepAliveCheckTime: 30 * time.Second,
		in:                 make(chan *event.Event),
		unpauseChan:        make(chan struct{}),
	}

	p.Refresh(conf)

	logrus.Debug("Starting expiration checker")
	go p.checkExpired()

	return p
}

// refresh load all config params that don't require a restart
func (p *Pipeline) Refresh(conf *config.AppConfig) {
	p.pause()

	// if the config has changed at all, refresh the index
	if p.config == nil || string(conf.Hash) != string(p.config.Hash) {
		go p.tracker.Start()
	}

	// update optional config options
	if conf.Escalations != nil {
		p.escalations = conf.Escalations
	}

	if conf.EventProviders != nil {
		p.providers = *conf.EventProviders
	}

	p.policies = conf.Policies
	p.keepAliveAge = conf.KeepAliveAge
	p.globalPolicy = conf.GlobalPolicy

	if p.globalPolicy != nil {
		p.globalPolicy.Compile()
	}

	// update to the new config
	p.config = conf
	p.unpause()
	// start up all of the providers
	logrus.Infof("Starting %d providers", len(p.providers.Collection))
	for name, ep := range p.providers.Collection {
		logrus.Infof("Starting event provider %s", name)
		go ep.Start(p.in)
	}
}

// unpause resume processing jobs
func (p *Pipeline) unpause() {
	logrus.Info("Unpausing pipeline")
	p.unpauseChan <- struct{}{}
	<-p.unpauseChan
}

// pause stop processing events
func (p *Pipeline) pause() {
	logrus.Info("Pausing pipeline")

	// cache the old injest channel
	old := p.in

	// make a temporary channel to catch incomming events
	p.in = nil

	// make a channel to signal the end of the pause
	done := make(chan struct{})
	p.unpauseChan = done

	// start a new goroutine to catch the incomming events
	go func() {
		// make a map to cache the incomming events
		cache := make(map[*event.Event]struct{})
		var e *event.Event
		for {
			select {

			// start caching the events as they come in
			case e = <-old:
				logrus.Debugf("Caching event during pause %+v", *e)
				cache[e] = struct{}{}

			// when the pause is complete, revert to the old injestion channel
			case <-done:

				// set the cached event channel
				p.in = old

				// restart the pipeline
				p.Start()

				// empty the cache
				for e, _ = range cache {
					logrus.Debugf("Proccessing cached event after unpause %+v", *e)
					old <- e
				}

				// signal the unpause function that we are done with the unpause
				p.unpauseChan <- struct{}{}
				return
			}
		}
	}()
}

func (p *Pipeline) GetTracker() *Tracker {
	return p.tracker
}

func (p *Pipeline) GetConfig() *config.AppConfig {
	return p.config
}

func (p *Pipeline) checkExpired() {
	var events []*event.Event
	for {
		time.Sleep(p.keepAliveCheckTime)
		logrus.Info("Checking for expired events.")

		// get keepalive events for all known hosts
		events = createKeepAliveEvents(p.tracker.HostTimes())
		logrus.Infof("Found %d hosts with keepalives", len(events))

		// process every event as if it was an incomming event
		for _, e := range events {
			p.Process(e)
		}
	}
}

// create keep alive events for each hostname -> time pair
func createKeepAliveEvents(times map[string]time.Time) []*event.Event {
	n := time.Now()
	events := make([]*event.Event, len(times))
	x := 0
	for host, t := range times {
		events[x] = &event.Event{
			Host:    host,
			Metric:  n.Sub(t).Seconds(),
			Service: KEEP_ALIVE_SERVICE_NAME,
		}
		x += 1
	}

	return events
}

func (p *Pipeline) Start() {
	logrus.Info("Starting pipeline")

	// fan in all of the providers and process them
	go func() {
		var e *event.Event
		for {

			// if the injest channel is nil, stop
			if p.in == nil {
				logrus.Info("Pipeline is paused, stoping pipeline")
				return
			}

			// recieve the event
			e = <-p.in

			logrus.Debugf("Beginning processing %+v", e)
			// process the event
			p.Process(e)
			logrus.Debugf("Done processing %+v", e)
		}
	}()
}

// Run the given event though the pipeline
func (p *Pipeline) Process(e *event.Event) int {
	if p.globalPolicy != nil {
		if !p.globalPolicy.CheckMatch(e) || !p.globalPolicy.CheckNotMatch(e) {
			return event.OK
		}
	}

	// track stas for this event
	p.tracker.TrackEvent(e)

	for _, pol := range p.policies {
		if pol.Matches(e) {
			act := pol.Action(e)

			// if there is an action to be taken
			if act != "" {

				// create a new incident for this event
				in := p.NewIncident(pol.Name, e)

				// dedup the incident
				if p.Dedupe(in) {

					// update the incident in the index
					if in.Status != event.OK {
						p.index.PutIncident(in)
					} else {
						p.index.DeleteIncidentById(in.IndexName())
					}

					// fetch the escalation to take
					esc, ok := p.escalations.Collection()[act]
					if ok {

						// send to every alarm in the escalation
						for _, a := range esc {
							a.Send(in)
						}
					} else {
						log.Println("unknown escalation", act)
					}
				}
			}
		}
	}

	return e.Status
}

// returns true if this is a new incident, false if it is a duplicate
func (p *Pipeline) Dedupe(i *event.Incident) bool {
	old := p.index.GetIncident(i.IndexName())

	if old == nil {
		return i.Status != event.OK
	}

	return old.Status != i.Status
}

func (p *Pipeline) ListIncidents() []*event.Incident {
	return p.index.ListIncidents()
}

func (p *Pipeline) PutIncident(in *event.Incident) {
	if in.Id == 0 {
		in.Id = p.index.GetIncidentCounter()
		p.index.UpdateIncidentCounter(in.Id + 1)
	}
	p.index.PutIncident(in)
}

func (p *Pipeline) NewIncident(policy string, e *event.Event) *event.Incident {
	return event.NewIncident(policy, e)
}
<commit_msg>fixed tests<commit_after>package pipeline

import (
	"log"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/eliothedeman/bangarang/alarm"
	"github.com/eliothedeman/bangarang/config"
	"github.com/eliothedeman/bangarang/event"
	"github.com/eliothedeman/bangarang/provider"
)

const (
	KEEP_ALIVE_SERVICE_NAME = "KeepAlive"
)

type Pipeline struct {
	keepAliveAge       time.Duration
	keepAliveCheckTime time.Duration
	globalPolicy       *alarm.Policy
	escalations        *alarm.Collection
	policies           map[string]*alarm.Policy
	index              *event.Index
	providers          provider.EventProviderCollection
	encodingPool       *event.EncodingPool
	config             *config.AppConfig
	tracker            *Tracker
	pauseCache         map[*event.Event]struct{}
	unpauseChan        chan struct{}
	in                 chan *event.Event
}

func NewPipeline(conf *config.AppConfig) *Pipeline {
	p := &Pipeline{
		encodingPool:       event.NewEncodingPool(event.EncoderFactories[conf.Encoding], event.DecoderFactories[conf.Encoding], runtime.NumCPU()),
		keepAliveAge:       conf.KeepAliveAge,
		keepAliveCheckTime: 30 * time.Second,
		in:                 make(chan *event.Event),
		unpauseChan:        make(chan struct{}),
		tracker:            NewTracker(),
		index:              event.NewIndex(),
	}

	p.Refresh(conf)

	logrus.Debug("Starting expiration checker")
	go p.checkExpired()

	return p
}

// refresh load all config params that don't require a restart
func (p *Pipeline) Refresh(conf *config.AppConfig) {
	p.pause()

	// if the config has changed at all, refresh the index
	if p.config == nil || string(conf.Hash) != string(p.config.Hash) {
		go p.tracker.Start()
	}

	// update optional config options
	if conf.Escalations != nil {
		p.escalations = conf.Escalations
	}

	if conf.EventProviders != nil {
		p.providers = *conf.EventProviders
	}

	p.policies = conf.Policies
	p.keepAliveAge = conf.KeepAliveAge
	p.globalPolicy = conf.GlobalPolicy

	if p.globalPolicy != nil {
		p.globalPolicy.Compile()
	}

	// update to the new config
	p.config = conf
	p.unpause()
	// start up all of the providers
	logrus.Infof("Starting %d providers", len(p.providers.Collection))
	for name, ep := range p.providers.Collection {
		logrus.Infof("Starting event provider %s", name)
		go ep.Start(p.in)
	}
}

// unpause resume processing jobs
func (p *Pipeline) unpause() {
	logrus.Info("Unpausing pipeline")
	p.unpauseChan <- struct{}{}
	<-p.unpauseChan
}

// pause stop processing events
func (p *Pipeline) pause() {
	logrus.Info("Pausing pipeline")

	// cache the old injest channel
	old := p.in

	// make a temporary channel to catch incomming events
	p.in = nil

	// make a channel to signal the end of the pause
	done := make(chan struct{})
	p.unpauseChan = done

	// start a new goroutine to catch the incomming events
	go func() {
		// make a map to cache the incomming events
		cache := make(map[*event.Event]struct{})
		var e *event.Event
		for {
			select {

			// start caching the events as they come in
			case e = <-old:
				logrus.Debugf("Caching event during pause %+v", *e)
				cache[e] = struct{}{}

			// when the pause is complete, revert to the old injestion channel
			case <-done:

				// set the cached event channel
				p.in = old

				// restart the pipeline
				p.Start()

				// empty the cache
				for e, _ = range cache {
					logrus.Debugf("Proccessing cached event after unpause %+v", *e)
					old <- e
				}

				// signal the unpause function that we are done with the unpause
				p.unpauseChan <- struct{}{}
				return
			}
		}
	}()
}

func (p *Pipeline) GetTracker() *Tracker {
	return p.tracker
}

func (p *Pipeline) GetConfig() *config.AppConfig {
	return p.config
}

func (p *Pipeline) checkExpired() {
	var events []*event.Event
	for {
		time.Sleep(p.keepAliveCheckTime)
		logrus.Info("Checking for expired events.")

		// get keepalive events for all known hosts
		events = createKeepAliveEvents(p.tracker.HostTimes())
		logrus.Infof("Found %d hosts with keepalives", len(events))

		// process every event as if it was an incomming event
		for _, e := range events {
			p.Process(e)
		}
	}
}

// create keep alive events for each hostname -> time pair
func createKeepAliveEvents(times map[string]time.Time) []*event.Event {
	n := time.Now()
	events := make([]*event.Event, len(times))
	x := 0
	for host, t := range times {
		events[x] = &event.Event{
			Host:    host,
			Metric:  n.Sub(t).Seconds(),
			Service: KEEP_ALIVE_SERVICE_NAME,
		}
		x += 1
	}

	return events
}

func (p *Pipeline) Start() {
	logrus.Info("Starting pipeline")

	// fan in all of the providers and process them
	go func() {
		var e *event.Event
		for {

			// if the injest channel is nil, stop
			if p.in == nil {
				logrus.Info("Pipeline is paused, stoping pipeline")
				return
			}

			// recieve the event
			e = <-p.in

			logrus.Debugf("Beginning processing %+v", e)
			// process the event
			p.Process(e)
			logrus.Debugf("Done processing %+v", e)
		}
	}()
}

// Run the given event though the pipeline
func (p *Pipeline) Process(e *event.Event) int {
	if p.globalPolicy != nil {
		if !p.globalPolicy.CheckMatch(e) || !p.globalPolicy.CheckNotMatch(e) {
			return event.OK
		}
	}

	// track stas for this event
	p.tracker.TrackEvent(e)

	for _, pol := range p.policies {
		if pol.Matches(e) {
			act := pol.Action(e)

			// if there is an action to be taken
			if act != "" {

				// create a new incident for this event
				in := p.NewIncident(pol.Name, e)

				// dedup the incident
				if p.Dedupe(in) {

					// update the incident in the index
					if in.Status != event.OK {
						p.index.PutIncident(in)
					} else {
						p.index.DeleteIncidentById(in.IndexName())
					}

					// fetch the escalation to take
					esc, ok := p.escalations.Collection()[act]
					if ok {

						// send to every alarm in the escalation
						for _, a := range esc {
							a.Send(in)
						}
					} else {
						log.Println("unknown escalation", act)
					}
				}
			}
		}
	}

	return e.Status
}

// returns true if this is a new incident, false if it is a duplicate
func (p *Pipeline) Dedupe(i *event.Incident) bool {
	old := p.index.GetIncident(i.IndexName())

	if old == nil {
		return i.Status != event.OK
	}

	return old.Status != i.Status
}

func (p *Pipeline) ListIncidents() []*event.Incident {
	return p.index.ListIncidents()
}

func (p *Pipeline) PutIncident(in *event.Incident) {
	if in.Id == 0 {
		in.Id = p.index.GetIncidentCounter()
		p.index.UpdateIncidentCounter(in.Id + 1)
	}
	p.index.PutIncident(in)
}

func (p *Pipeline) NewIncident(policy string, e *event.Event) *event.Incident {
	return event.NewIncident(policy, e)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"sort"
	"time"

	"github.com/parnurzeal/gorequest"
	"github.com/olorin/nagiosplugin"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	servers = kingpin.Arg("servers", "comma separated list of HTTP servers").Required().String()
	concurrent = kingpin.Flag("concurrent", "max number of concurrent HTTP requests").Default("100").Short('c').Int()
	timeout = kingpin.Flag("timeout", "timeout for HTTP requests").Default("10").Short('t').Int()
	url = kingpin.Flag("url", "URL").Default("http://stalker.wikia.com/wiki/Main_Page").Short('u').String()
)

type Msg struct {
	host string
	etag interface{}
}

type Pair struct {
	Key string
	Value int
}

type Param struct {
	host string
	url string
}

type PairList []Pair

func (p PairList) Len() int { return len(p) }
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }

func worker(id int, jobs <-chan Param, results chan<- Msg) {
	for j := range jobs {
		var msg Msg
		msg.host = j.host

		proxyUrl := "http://" + j.host + ":80"
		request := gorequest.New().Proxy(proxyUrl).Timeout(time.Duration(*timeout) * time.Second)
		resp, _, err := request.Get(j.url).
			Set("X-Wikia-Internal-Request", `nagios`).
			End()
		if err != nil {
			msg.etag = nil
			results <- msg
			continue
		}
		if (resp.StatusCode != 200) {
			msg.etag = nil
			results <- msg
			continue
		}

		if etag, ok := resp.Header["Etag"]; ok {
			msg.etag = etag[0]
			results <- msg
		} else {
			msg.etag = nil
			results <- msg
		}
	}
}

func executeWorkers(hosts, url string, concurrent int) []Msg {
	hostsList := strings.Split(hosts, ",")

	jobs := make(chan Param, len(hostsList))
	results := make(chan Msg, len(hostsList))

	var workersNum int
	if (len(hostsList) < concurrent) {
		workersNum = len(hostsList)
	} else {
		workersNum = concurrent
	}
	for w := 1; w <= workersNum; w++ {
		go worker(w, jobs, results)
	}

	for _, h := range hostsList {
		var p Param
		p.host = h
		p.url = url
		jobs <- p
	}
	close(jobs)

	var resultList []Msg
	for r := 1; r <= len(hostsList); r++ {
		resultList = append(resultList, <- results)
	}
	return resultList
}

func analizeResults(messages []Msg, check *nagiosplugin.Check) {
	hostsPerEtag := make(map[string][]string)
	failedHosts := make([]string, 0)
	for _, msg := range messages {
		if msg.etag == nil {
			failedHosts = append(failedHosts, msg.host)
		} else {
			hostsPerEtag[msg.etag.(string)] = append(hostsPerEtag[msg.etag.(string)], msg.host)
		}
	}

	// if there are more than one ETag found
	if (len(hostsPerEtag) > 1) {
		numOfEtags := make(map[string]int)
		for k, _ := range hostsPerEtag {
			numOfEtags[k] = len(hostsPerEtag[k])
		}
		sortedNumOfEtags := sortByValue(numOfEtags)
		// delete 0 element of slice
		sortedNumOfEtags = append(sortedNumOfEtags[:0], sortedNumOfEtags[1:]...)

		invalidHosts := make([]string, 0)
		for _, h := range sortedNumOfEtags {
			invalidHosts = append(invalidHosts, hostsPerEtag[h.Key]...)
		}
		check.AddResult(nagiosplugin.CRITICAL, fmt.Sprintf("invalid ETag on: %s", strings.Join(append(invalidHosts, failedHosts...), " ")))
	} else if (len(hostsPerEtag) == 0) {
		check.AddResult(nagiosplugin.WARNING, "no ETags")
	} else if (len(hostsPerEtag) == 1) {
		var etagsNum int
		for k, _ := range hostsPerEtag {
			etagsNum = len(hostsPerEtag[k])
			break
		}
		if (len(failedHosts) == 0) {
			check.AddResult(nagiosplugin.OK, fmt.Sprintf("%d identical ETags", etagsNum))
		} else {
			check.AddResult(nagiosplugin.WARNING, fmt.Sprintf("no ETags from: %s", strings.Join(failedHosts, " ")))
		}
	}
}

func sortByValue(m map[string]int) PairList {
	p := make(PairList, len(m))

	i := 0
	for k, v := range m {
		p[i] = Pair{k, v}
		i++
	}
	sort.Sort(sort.Reverse(p))
	return p
}

func main() {
	kingpin.Parse()

	check := nagiosplugin.NewCheck()
	defer check.Finish()

	analizeResults(executeWorkers(*servers, *url, *concurrent), check)
}
<commit_msg>cosmetics<commit_after>package main

import (
	"fmt"
	"strings"
	"sort"
	"time"

	"github.com/parnurzeal/gorequest"
	"github.com/olorin/nagiosplugin"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	servers = kingpin.Arg("servers", "comma separated list of HTTP servers").Required().String()
	concurrent = kingpin.Flag("concurrent", "max number of concurrent HTTP requests").Default("100").Short('c').Int()
	timeout = kingpin.Flag("timeout", "timeout for HTTP requests").Default("10").Short('t').Int()
	url = kingpin.Flag("url", "URL").Default("http://stalker.wikia.com/wiki/Main_Page").Short('u').String()
)

type Msg struct {
	host string
	etag interface{}
}

type Pair struct {
	Key string
	Value int
}

type Param struct {
	host string
	url string
}

type PairList []Pair

func (p PairList) Len() int { return len(p) }
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }

func worker(id int, jobs <-chan Param, results chan<- Msg) {
	for j := range jobs {
		var msg Msg
		msg.host = j.host

		proxyUrl := "http://" + j.host + ":80"
		request := gorequest.New().Proxy(proxyUrl).Timeout(time.Duration(*timeout) * time.Second)
		resp, _, err := request.Get(j.url).
			Set("X-Wikia-Internal-Request", `nagios`).
			End()
		if err != nil {
			msg.etag = nil
			results <- msg
			continue
		}
		if resp.StatusCode != 200 {
			msg.etag = nil
			results <- msg
			continue
		}

		if etag, ok := resp.Header["Etag"]; ok {
			msg.etag = etag[0]
			results <- msg
		} else {
			msg.etag = nil
			results <- msg
		}
	}
}

func executeWorkers(hosts, url string, concurrent int) []Msg {
	hostsList := strings.Split(hosts, ",")

	jobs := make(chan Param, len(hostsList))
	results := make(chan Msg, len(hostsList))

	var workersNum int
	if len(hostsList) < concurrent {
		workersNum = len(hostsList)
	} else {
		workersNum = concurrent
	}
	for w := 1; w <= workersNum; w++ {
		go worker(w, jobs, results)
	}

	for _, h := range hostsList {
		var p Param
		p.host = h
		p.url = url
		jobs <- p
	}
	close(jobs)

	var resultList []Msg
	for r := 1; r <= len(hostsList); r++ {
		resultList = append(resultList, <- results)
	}
	return resultList
}

func analizeResults(messages []Msg, check *nagiosplugin.Check) {
	hostsPerEtag := make(map[string][]string)
	failedHosts := make([]string, 0)
	for _, msg := range messages {
		if msg.etag == nil {
			failedHosts = append(failedHosts, msg.host)
		} else {
			hostsPerEtag[msg.etag.(string)] = append(hostsPerEtag[msg.etag.(string)], msg.host)
		}
	}

	// if there are more than one ETag found
	if len(hostsPerEtag) > 1 {
		numOfEtags := make(map[string]int)
		for k, _ := range hostsPerEtag {
			numOfEtags[k] = len(hostsPerEtag[k])
		}
		sortedNumOfEtags := sortByValue(numOfEtags)
		// delete 0 element of slice
		sortedNumOfEtags = append(sortedNumOfEtags[:0], sortedNumOfEtags[1:]...)

		invalidHosts := make([]string, 0)
		for _, h := range sortedNumOfEtags {
			invalidHosts = append(invalidHosts, hostsPerEtag[h.Key]...)
		}
		check.AddResult(nagiosplugin.CRITICAL, fmt.Sprintf("invalid ETag on: %s", strings.Join(append(invalidHosts, failedHosts...), " ")))
	} else if len(hostsPerEtag) == 0 {
		check.AddResult(nagiosplugin.WARNING, "no ETags")
	} else if len(hostsPerEtag) == 1 {
		var etagsNum int
		for k, _ := range hostsPerEtag {
			etagsNum = len(hostsPerEtag[k])
			break
		}
		if len(failedHosts) == 0 {
			check.AddResult(nagiosplugin.OK, fmt.Sprintf("%d identical ETags", etagsNum))
		} else {
			check.AddResult(nagiosplugin.WARNING, fmt.Sprintf("no ETags from: %s", strings.Join(failedHosts, " ")))
		}
	}
}

func sortByValue(m map[string]int) PairList {
	p := make(PairList, len(m))

	i := 0
	for k, v := range m {
		p[i] = Pair{k, v}
		i++
	}
	sort.Sort(sort.Reverse(p))
	return p
}

func main() {
	kingpin.Parse()

	check := nagiosplugin.NewCheck()
	defer check.Finish()

	analizeResults(executeWorkers(*servers, *url, *concurrent), check)
}
<|endoftext|>


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

package main

import (
	"errors"
	"fmt"
	"github.com/01org/ciao/ciao-controller/types"
	"github.com/golang/glog"
	"time"
)

func (c *controller) evacuateNode(nodeID string) error {
	// should I bother to see if nodeID is valid?
	go c.client.EvacuateNode(nodeID)
	return nil
}

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	// get node id.  If there is no node id we can't send a restart
	nodeID, state, err := c.ds.GetInstanceInfo(instanceID)
	if err != nil {
		return err
	}

	if nodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if state != "exited" {
		return errors.New("You may only restart paused instances")
	}

	go c.client.RestartInstance(instanceID, nodeID)
	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	nodeID, state, err := c.ds.GetInstanceInfo(instanceID)
	if err != nil {
		return err
	}

	if nodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if state == "pending" {
		return errors.New("You may not stop a pending instance")
	}

	go c.client.StopInstance(instanceID, nodeID)
	return nil
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	nodeID, _, err := c.ds.GetInstanceInfo(instanceID)
	if err != nil {
		return err
	}

	if nodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	go c.client.DeleteInstance(instanceID, nodeID)
	return nil
}

func (c *controller) startWorkload(workloadID string, tenantID string, instances int, trace bool, label string) ([]*types.Instance, error) {
	var e error

	if instances == 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(workloadID)
	if err != nil {
		return nil, err
	}

	if !isCNCIWorkload(wl) {
		tenant, err := c.ds.GetTenant(tenantID)
		if err != nil {
			return nil, err
		}

		if tenant == nil {
			if *noNetwork {
				_, err := c.ds.AddTenant(tenantID)
				if err != nil {
					return nil, err
				}
			} else {

				err = c.addTenant(tenantID)
				if err != nil {
					return nil, err
				}
			}
		} else if tenant.CNCIIP == "" {
			if !*noNetwork {
				_ = c.addTenant(tenantID)
				tenant, err = c.ds.GetTenant(tenantID)
				if tenant.CNCIIP == "" {
					return nil, errors.New("Unable to Launch Tenant CNCI")
				}
			}
		}
	}

	var newInstances []*types.Instance

	for i := 0; i < instances; i++ {
		startTime := time.Now()
		instance, err := newInstance(c, tenantID, wl)
		if err != nil {
			glog.V(2).Info("error newInstance")
			e = err
			continue
		}
		instance.startTime = startTime

		ok, err := instance.Allowed()
		if ok {
			err = instance.Add()
			if err != nil {
				glog.V(2).Info("error adding instance")
				instance.Clean()
				e = err
				continue
			}

			newInstances = append(newInstances, &instance.Instance)
			if trace == false {
				go c.client.StartWorkload(instance.newConfig.config)
			} else {
				go c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, label)
			}
		} else {
			instance.Clean()
			if err != nil {
				e = err
				continue
			} else {
				// stop if we are over limits
				return nil, errors.New("Over Tenant Limits")
			}
		}
	}

	return newInstances, e
}

func (c *controller) launchCNCI(tenantID string) (err error) {
	workloadID, err := c.ds.GetCNCIWorkloadID()
	if err != nil {
		return err
	}

	ch := make(chan bool)

	c.ds.AddTenantChan(ch, tenantID)

	_, err = c.startWorkload(workloadID, tenantID, 1, false, "")
	if err != nil {
		return err
	}

	success := <-ch

	if success {
		return nil
	}
	msg := fmt.Sprintf("Failed to Launch CNCI for %s", tenantID)
	return errors.New(msg)
}

func (c *controller) addTenant(id string) error {
	// create new entry in datastore
	_, err := c.ds.AddTenant(id)
	if err != nil {
		return err
	}

	// start up a CNCI. this will block till the
	// CNCI started event is returned
	return c.launchCNCI(id)
}
<commit_msg>ciao-controller: command: make return values explicit<commit_after>/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

package main

import (
	"errors"
	"fmt"
	"github.com/01org/ciao/ciao-controller/types"
	"github.com/golang/glog"
	"time"
)

func (c *controller) evacuateNode(nodeID string) error {
	// should I bother to see if nodeID is valid?
	go c.client.EvacuateNode(nodeID)
	return nil
}

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	// get node id.  If there is no node id we can't send a restart
	nodeID, state, err := c.ds.GetInstanceInfo(instanceID)
	if err != nil {
		return err
	}

	if nodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if state != "exited" {
		return errors.New("You may only restart paused instances")
	}

	go c.client.RestartInstance(instanceID, nodeID)
	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	nodeID, state, err := c.ds.GetInstanceInfo(instanceID)
	if err != nil {
		return err
	}

	if nodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if state == "pending" {
		return errors.New("You may not stop a pending instance")
	}

	go c.client.StopInstance(instanceID, nodeID)
	return nil
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	nodeID, _, err := c.ds.GetInstanceInfo(instanceID)
	if err != nil {
		return err
	}

	if nodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	go c.client.DeleteInstance(instanceID, nodeID)
	return nil
}

func (c *controller) startWorkload(workloadID string, tenantID string, instances int, trace bool, label string) ([]*types.Instance, error) {
	var e error

	if instances == 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(workloadID)
	if err != nil {
		return nil, err
	}

	if !isCNCIWorkload(wl) {
		tenant, err := c.ds.GetTenant(tenantID)
		if err != nil {
			return nil, err
		}

		if tenant == nil {
			if *noNetwork {
				_, err := c.ds.AddTenant(tenantID)
				if err != nil {
					return nil, err
				}
			} else {

				err = c.addTenant(tenantID)
				if err != nil {
					return nil, err
				}
			}
		} else if tenant.CNCIIP == "" {
			if !*noNetwork {
				_ = c.addTenant(tenantID)
				tenant, err = c.ds.GetTenant(tenantID)
				if tenant.CNCIIP == "" {
					return nil, errors.New("Unable to Launch Tenant CNCI")
				}
			}
		}
	}

	var newInstances []*types.Instance

	for i := 0; i < instances; i++ {
		startTime := time.Now()
		instance, err := newInstance(c, tenantID, wl)
		if err != nil {
			glog.V(2).Info("error newInstance")
			e = err
			continue
		}
		instance.startTime = startTime

		ok, err := instance.Allowed()
		if ok {
			err = instance.Add()
			if err != nil {
				glog.V(2).Info("error adding instance")
				instance.Clean()
				e = err
				continue
			}

			newInstances = append(newInstances, &instance.Instance)
			if trace == false {
				go c.client.StartWorkload(instance.newConfig.config)
			} else {
				go c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, label)
			}
		} else {
			instance.Clean()
			if err != nil {
				e = err
				continue
			} else {
				// stop if we are over limits
				return nil, errors.New("Over Tenant Limits")
			}
		}
	}

	return newInstances, e
}

func (c *controller) launchCNCI(tenantID string) error {
	workloadID, err := c.ds.GetCNCIWorkloadID()
	if err != nil {
		return err
	}

	ch := make(chan bool)

	c.ds.AddTenantChan(ch, tenantID)

	_, err = c.startWorkload(workloadID, tenantID, 1, false, "")
	if err != nil {
		return err
	}

	success := <-ch

	if success {
		return nil
	}
	msg := fmt.Sprintf("Failed to Launch CNCI for %s", tenantID)
	return errors.New(msg)
}

func (c *controller) addTenant(id string) error {
	// create new entry in datastore
	_, err := c.ds.AddTenant(id)
	if err != nil {
		return err
	}

	// start up a CNCI. this will block till the
	// CNCI started event is returned
	return c.launchCNCI(id)
}
<|endoftext|>


<commit_before>package example

import (
	"bufio"
	"fmt"

	"github.com/iris-contrib/middleware/basicauth"
	"github.com/kataras/iris"
	"github.com/valyala/fasthttp"
)

// IrisHandler creates fasthttp.RequestHandler using Iris web framework.
func IrisHandler() fasthttp.RequestHandler {
	api := iris.New()

	api.Get("/things", func(c *iris.Context) {
		c.JSON(iris.StatusOK, []interface{}{
			iris.Map{
				"name":        "foo",
				"description": "foo thing",
			},
			iris.Map{
				"name":        "bar",
				"description": "bar thing",
			},
		})
	})

	api.Post("/redirect", func(c *iris.Context) {
		c.Redirect("/things", iris.StatusFound)
	})

	api.Get("/params/:x/:y", func(c *iris.Context) {
		c.JSON(iris.StatusOK, iris.Map{
			"x":  c.Param("x"),
			"y":  c.Param("y"),
			"q":  c.URLParam("q"),
			"p1": c.FormValueString("p1"),
			"p2": c.FormValueString("p2"),
		})
	})

	auth := basicauth.Default(map[string]string{
		"ford": "betelgeuse7",
	})

	api.Get("/auth", auth, func(c *iris.Context) {
		c.Write("authenticated!")
	})

	api.Post("/session/set", func(c *iris.Context) {
		sess := iris.Map{}

		if err := c.ReadJSON(&sess); err != nil {
			panic(err.Error())
		}

		c.Session().Set("name", sess["name"])
	})

	api.Get("/session/get", func(c *iris.Context) {
		name := c.Session().GetString("name")

		c.JSON(iris.StatusOK, iris.Map{
			"name": name,
		})
	})

	api.Get("/stream", func(c *iris.Context) {
		c.StreamWriter(func(w *bufio.Writer) {
			for i := 0; i < 10; i++ {
				fmt.Fprintf(w, "%d", i)

				if err := w.Flush(); err != nil {
					return
				}
			}
		})
	})

	api.Post("/stream", func(c *iris.Context) {
		c.Write(string(c.Request.Body()))
	})

	sub := api.Party("subdomain.")

	sub.Post("/set", func(c *iris.Context) {
		c.Session().Set("message", "hello from subdomain")
	})

	sub.Get("/get", func(c *iris.Context) {
		c.Write(c.Session().GetString("message"))
	})

	return api.ListenVirtual().Handler
}
<commit_msg>Update for iris v4.4.0<commit_after>package example

import (
	"bufio"
	"fmt"

	"github.com/iris-contrib/middleware/basicauth"
	"github.com/kataras/iris"
	"github.com/valyala/fasthttp"
)

// IrisHandler creates fasthttp.RequestHandler using Iris web framework.
func IrisHandler() fasthttp.RequestHandler {
	api := iris.New()

	api.Get("/things", func(c *iris.Context) {
		c.JSON(iris.StatusOK, []interface{}{
			iris.Map{
				"name":        "foo",
				"description": "foo thing",
			},
			iris.Map{
				"name":        "bar",
				"description": "bar thing",
			},
		})
	})

	api.Post("/redirect", func(c *iris.Context) {
		c.Redirect("/things", iris.StatusFound)
	})

	api.Get("/params/:x/:y", func(c *iris.Context) {
		c.JSON(iris.StatusOK, iris.Map{
			"x":  c.Param("x"),
			"y":  c.Param("y"),
			"q":  c.URLParam("q"),
			"p1": c.FormValueString("p1"),
			"p2": c.FormValueString("p2"),
		})
	})

	auth := basicauth.Default(map[string]string{
		"ford": "betelgeuse7",
	})

	api.Get("/auth", auth, func(c *iris.Context) {
		c.Write("authenticated!")
	})

	api.Post("/session/set", func(c *iris.Context) {
		sess := iris.Map{}

		if err := c.ReadJSON(&sess); err != nil {
			panic(err.Error())
		}

		c.Session().Set("name", sess["name"])
	})

	api.Get("/session/get", func(c *iris.Context) {
		name := c.Session().GetString("name")

		c.JSON(iris.StatusOK, iris.Map{
			"name": name,
		})
	})

	api.Get("/stream", func(c *iris.Context) {
		c.StreamWriter(func(w *bufio.Writer) {
			for i := 0; i < 10; i++ {
				fmt.Fprintf(w, "%d", i)

				if err := w.Flush(); err != nil {
					return
				}
			}
		})
	})

	api.Post("/stream", func(c *iris.Context) {
		c.Write(string(c.Request.Body()))
	})

	sub := api.Party("subdomain.")

	sub.Post("/set", func(c *iris.Context) {
		c.Session().Set("message", "hello from subdomain")
	})

	sub.Get("/get", func(c *iris.Context) {
		c.Write(c.Session().GetString("message"))
	})

	api.Build()
	return api.Router
}
<|endoftext|>


<commit_before>package dispatcher

import (
	"database/sql"
	"encoding/hex"
	"errors"
	"fmt"

	"getmelange.com/dap"

	"airdispat.ch/crypto"
	"airdispat.ch/identity"
	"airdispat.ch/message"
	"airdispat.ch/server"
	"airdispat.ch/tracker"
	"github.com/coopernurse/gorp"

	// Imported for DB Initialization
	_ "github.com/lib/pq"
)

func createError(location string, error error) *server.ServerError {
	return &server.ServerError{
		Location: location,
		Error:    error,
	}
}

// Server exposes a structure that holds all the properties of an AirDisaptch
// dispatcher.
type Server struct {
	// Basic Properties
	Me      string
	KeyFile string
	Key     *identity.Identity

	// Tracker Properties
	TrackerURL string
	Alias      string

	// Database
	DBConn string
	DBType string
	dbmap  *gorp.DbMap

	// Import Logging and Such
	server.BasicServer
}

// Run will start the server with the specified database model.
func (m *Server) Run(port int) error {
	// Initialize Type of Database
	var dialect gorp.Dialect
	switch m.DBType {
	case "postgres":
		dialect = gorp.PostgresDialect{}
	case "sqlite":
		dialect = gorp.SqliteDialect{}
	case "mysql":
		dialect = gorp.MySQLDialect{}
	case "mssql":
		dialect = gorp.SqlServerDialect{}
	default:
		return errors.New("Unable to determine DB dialect.")
	}

	// Initialize Database Connection
	db, err := sql.Open(m.DBType, m.DBConn)
	if err != nil {
		return err
	}

	// Initialize Gorp
	m.dbmap = &gorp.DbMap{
		Db:      db,
		Dialect: dialect,
	}
	// Create Tables for Messages
	m.CreateTables()
	err = m.dbmap.CreateTablesIfNotExists()
	if err != nil {
		return err
	}

	// Load the Server Keys
	loadedKey, err := identity.LoadKeyFromFile(m.KeyFile)
	if err != nil {
		loadedKey, err = identity.CreateIdentity()
		if err != nil {
			return err
		}
		if m.KeyFile != "" {
			err = loadedKey.SaveKeyToFile(m.KeyFile)
			if err != nil {
				return err
			}
		}
	}
	m.LogMessage("Loaded Address", loadedKey.Address.String())
	m.LogMessage("Loaded Encryption Key", hex.EncodeToString(crypto.RSAToBytes(loadedKey.Address.EncryptionKey)))

	loadedKey.SetLocation(m.Me)
	err = (&tracker.Router{
		URL:    m.TrackerURL,
		Origin: loadedKey,
	}).Register(loadedKey, m.Alias, nil)
	if err != nil {
		return err
	}

	m.Key = loadedKey

	handlers := []server.Handler{
		&dap.Handler{
			Key:      loadedKey,
			Delegate: m,
		},
	}

	// Create the AirDispatch Server
	adServer := server.Server{
		LocationName: m.Me,
		Key:          loadedKey,
		Delegate:     m,
		Handlers:     handlers,
		// Router:       mailserver.LookupRouter,
	}

	return adServer.StartServer(fmt.Sprintf("%d", port))
}

// SaveMessageDescription will serialize a message description and add it to the
// database.
func (m *Server) SaveMessageDescription(alert *message.EncryptedMessage) {
	err := m.SaveIncomingMessage(alert)
	if err != nil {
		m.HandleError(createError("Saving new alert to db.", err))
	}
}

// RetrieveMessageForUser will retrieve the message stored for the specified user
// at the specified name.
func (m *Server) RetrieveMessageForUser(name string, author *identity.Address, forAddr *identity.Address) *message.EncryptedMessage {
	msg, err := m.GetOutgoingMessageWithName(name, author.String(), forAddr.String())
	if err != nil {
		m.HandleError(createError("Getting message from DB.", err))
		return nil
	}

	data, err := msg.ToDispatch(forAddr.String())
	if err != nil {
		m.HandleError(createError("(RetrieveMessage) Marshalling message", err))
		return nil
	}

	return data
}

// RetrieveMessageListForUser will return all the public messages that a User
// has access to sent after `since`.
func (m *Server) RetrieveMessageListForUser(since uint64, author *identity.Address, forAddr *identity.Address) []*message.EncryptedMessage {
	results, err := m.GetOutgoingPublicMessagesFor(since, author.String(), forAddr.String())
	if err != nil {
		m.HandleError(createError("Getting messages from DB.", err))
		return nil
	}

	var out []*message.EncryptedMessage

	for _, v := range results {
		d, err := v.ToDispatch(forAddr.String())
		if err != nil {
			m.HandleError(createError("(RetrieveMessageList) Marshalling message", err))
			continue
		}
		out = append(out, d)
	}

	return out
}
<commit_msg>Added Stub so Dispatcher will Compile with New AD Version<commit_after>package dispatcher

import (
	"database/sql"
	"encoding/hex"
	"errors"
	"fmt"
	"io"

	"getmelange.com/dap"

	"airdispat.ch/crypto"
	"airdispat.ch/identity"
	"airdispat.ch/message"
	"airdispat.ch/server"
	"airdispat.ch/tracker"
	"github.com/coopernurse/gorp"

	// Imported for DB Initialization
	_ "github.com/lib/pq"
)

func createError(location string, error error) *server.ServerError {
	return &server.ServerError{
		Location: location,
		Error:    error,
	}
}

// Server exposes a structure that holds all the properties of an AirDisaptch
// dispatcher.
type Server struct {
	// Basic Properties
	Me      string
	KeyFile string
	Key     *identity.Identity

	// Tracker Properties
	TrackerURL string
	Alias      string

	// Database
	DBConn string
	DBType string
	dbmap  *gorp.DbMap

	// Import Logging and Such
	server.BasicServer
}

// Run will start the server with the specified database model.
func (m *Server) Run(port int) error {
	// Initialize Type of Database
	var dialect gorp.Dialect
	switch m.DBType {
	case "postgres":
		dialect = gorp.PostgresDialect{}
	case "sqlite":
		dialect = gorp.SqliteDialect{}
	case "mysql":
		dialect = gorp.MySQLDialect{}
	case "mssql":
		dialect = gorp.SqlServerDialect{}
	default:
		return errors.New("Unable to determine DB dialect.")
	}

	// Initialize Database Connection
	db, err := sql.Open(m.DBType, m.DBConn)
	if err != nil {
		return err
	}

	// Initialize Gorp
	m.dbmap = &gorp.DbMap{
		Db:      db,
		Dialect: dialect,
	}
	// Create Tables for Messages
	m.CreateTables()
	err = m.dbmap.CreateTablesIfNotExists()
	if err != nil {
		return err
	}

	// Load the Server Keys
	loadedKey, err := identity.LoadKeyFromFile(m.KeyFile)
	if err != nil {
		loadedKey, err = identity.CreateIdentity()
		if err != nil {
			return err
		}
		if m.KeyFile != "" {
			err = loadedKey.SaveKeyToFile(m.KeyFile)
			if err != nil {
				return err
			}
		}
	}
	m.LogMessage("Loaded Address", loadedKey.Address.String())
	m.LogMessage("Loaded Encryption Key", hex.EncodeToString(crypto.RSAToBytes(loadedKey.Address.EncryptionKey)))

	loadedKey.SetLocation(m.Me)
	err = (&tracker.Router{
		URL:    m.TrackerURL,
		Origin: loadedKey,
	}).Register(loadedKey, m.Alias, nil)
	if err != nil {
		return err
	}

	m.Key = loadedKey

	handlers := []server.Handler{
		&dap.Handler{
			Key:      loadedKey,
			Delegate: m,
		},
	}

	// Create the AirDispatch Server
	adServer := server.Server{
		LocationName: m.Me,
		Key:          loadedKey,
		Delegate:     m,
		Handlers:     handlers,
		// Router:       mailserver.LookupRouter,
	}

	return adServer.StartServer(fmt.Sprintf("%d", port))
}

// SaveMessageDescription will serialize a message description and add it to the
// database.
func (m *Server) SaveMessageDescription(alert *message.EncryptedMessage) {
	err := m.SaveIncomingMessage(alert)
	if err != nil {
		m.HandleError(createError("Saving new alert to db.", err))
	}
}

func (m *Server) RetrieveDataForUser(name string, author *identity.Address, forAddr *identity.Address) (*message.EncryptedMessage, io.ReadCloser) {
	return nil, nil
}

// RetrieveMessageForUser will retrieve the message stored for the specified user
// at the specified name.
func (m *Server) RetrieveMessageForUser(name string, author *identity.Address, forAddr *identity.Address) *message.EncryptedMessage {
	msg, err := m.GetOutgoingMessageWithName(name, author.String(), forAddr.String())
	if err != nil {
		m.HandleError(createError("Getting message from DB.", err))
		return nil
	}

	data, err := msg.ToDispatch(forAddr.String())
	if err != nil {
		m.HandleError(createError("(RetrieveMessage) Marshalling message", err))
		return nil
	}

	return data
}

// RetrieveMessageListForUser will return all the public messages that a User
// has access to sent after `since`.
func (m *Server) RetrieveMessageListForUser(since uint64, author *identity.Address, forAddr *identity.Address) []*message.EncryptedMessage {
	results, err := m.GetOutgoingPublicMessagesFor(since, author.String(), forAddr.String())
	if err != nil {
		m.HandleError(createError("Getting messages from DB.", err))
		return nil
	}

	var out []*message.EncryptedMessage

	for _, v := range results {
		d, err := v.ToDispatch(forAddr.String())
		if err != nil {
			m.HandleError(createError("(RetrieveMessageList) Marshalling message", err))
			continue
		}
		out = append(out, d)
	}

	return out
}
<|endoftext|>


<commit_before>package influxhandler

import (
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/influxdb/influxdb/client"
)

type Config struct {
	// MaxSeries the maximum series kept before flushing them to InfluxDB
	MaxSeriesCount int
	// MaxDuration is the max duration we keep series before flushing them to InfluxDB
	MaxDuration time.Duration
}

type Middleware struct {
	// Name is the name used to store the serie
	name string
	// Client is the InfluxDB connection client
	client *client.Client
	// Options
	config Config
	// series stores the Series before flushing
	series []*client.Series
	// c is a channel fed with new series
	ch chan (*client.Series)
	// t is the last time we have flushed data
	lastF time.Time
	// protects the series
	sync.Mutex
}

// NewHandler create a new Handler that will log every http request into InfluxDB
// Each request generate a flush to the DB
func NewHandler(name string, c *client.Client) *Middleware {
	return NewBuferedHandler(name, c, Config{})
}

// NewBuferedHandler create a new Handler that will log every http request into InfluxDB
// Series will be flushed according to config options
func NewBuferedHandler(name string, c *client.Client, config Config) *Middleware {
	m := &Middleware{name: name,
		client: c,
		config: config,
		ch:     make(chan *client.Series),
		series: make([]*client.Series, config.MaxSeriesCount),
		lastF:  time.Now()}

	go func() {
		scopy := make([]*client.Series, m.config.MaxSeriesCount)
		timeout := make(chan bool, 1)

		for {

			if m.config.MaxDuration != 0 {
				go func() {
					time.Sleep(m.config.MaxDuration)
					timeout <- true
				}()
			}

			select {
			case s := <-m.ch:
				m.Lock()

				m.series = append(m.series, s)

				if len(m.series) >= m.config.MaxSeriesCount && m.config.MaxDuration != 0 && time.Since(m.lastF) < m.config.MaxDuration {
					copy(scopy, m.series)
					go func() {
						err := m.client.WriteSeries(scopy)
						if err != nil {
							log.Println("influxhandler", err)
						}
					}()
					m.lastF = time.Now()
					m.series = make([]*client.Series, config.MaxSeriesCount)
					copy(scopy, m.series)
				}
				m.Unlock()

			case <-timeout:
				// check if we need to flush
				m.Lock()

				if len(m.series) > 0 && time.Since(m.lastF) > m.config.MaxDuration {
					copy(scopy, m.series)
					go func() {
						err := m.client.WriteSeries(scopy)
						if err != nil {
							log.Println("influxhandler", err)
						}
					}()
					m.lastF = time.Now()
					m.series = make([]*client.Series, config.MaxSeriesCount)
					copy(scopy, m.series)
				}
				m.Unlock()
			}
		}

	}()

	return m
}

// writeSeries saves or bufferizes the series
func (m *Middleware) WriteSeries(s *client.Series) {
	if m.config.MaxDuration == 0 && m.config.MaxSeriesCount == 0 {
		err := m.client.WriteSeries([]*client.Series{s})
		if err != nil {
			log.Println("influxhandler", err)
		}
		return
	}
	m.ch <- s
}

// Handler logs the request as it goes in and the response as it goes out
func (m *Middleware) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if m.client == nil {
			h.ServeHTTP(w, req)
			return
		}
		start := time.Now()
		h.ServeHTTP(w, req)
		t := time.Since(start)
		s := &client.Series{
			Name:    "resp_time",
			Columns: []string{"duration", "code", "url", "method"},
			Points: [][]interface{}{
				// TODO: get the real status code
				[]interface{}{int64(t / time.Millisecond), 200, req.RequestURI, req.Method},
			},
		}
		m.WriteSeries(s)
	})
}
<commit_msg>documentation<commit_after>package influxhandler

import (
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/influxdb/influxdb/client"
)

type Config struct {
	// MaxSeries the maximum series kept before flushing them to InfluxDB
	MaxSeriesCount int
	// MaxDuration is the max duration we keep series before flushing them to InfluxDB
	MaxDuration time.Duration
}

type Middleware struct {
	// Name is the name used to store the serie
	name string
	// Client is the InfluxDB connection client
	client *client.Client
	// Options
	config Config
	// series stores the Series before flushing
	series []*client.Series
	// c is a channel fed with new series
	ch chan (*client.Series)
	// t is the last time we have flushed data
	lastF time.Time
	// mutex to protect the series, a FIFO channel is not safe
	// as the series may be dequeued by the timeout from another goroutine
	// TODO: could it be avoided by sending nil ch from the timeout ?
	sync.Mutex
}

// NewHandler create a new Handler that will log every http request into InfluxDB
// Each request generate a flush to the DB
func NewHandler(name string, c *client.Client) *Middleware {
	return NewBuferedHandler(name, c, Config{})
}

// NewBuferedHandler create a new Handler that will log every http request into InfluxDB
// Series will be flushed according to config options
func NewBuferedHandler(name string, c *client.Client, config Config) *Middleware {
	m := &Middleware{name: name,
		client: c,
		config: config,
		ch:     make(chan *client.Series),
		series: make([]*client.Series, config.MaxSeriesCount),
		lastF:  time.Now()}

	go func() {
		scopy := make([]*client.Series, m.config.MaxSeriesCount)
		timeout := make(chan bool, 1)

		for {

			if m.config.MaxDuration != 0 {
				go func() {
					time.Sleep(m.config.MaxDuration)
					timeout <- true
				}()
			}

			select {
			case s := <-m.ch:
				m.Lock()

				m.series = append(m.series, s)

				if len(m.series) >= m.config.MaxSeriesCount && m.config.MaxDuration != 0 && time.Since(m.lastF) < m.config.MaxDuration {
					copy(scopy, m.series)
					go func() {
						err := m.client.WriteSeries(scopy)
						if err != nil {
							log.Println("influxhandler", err)
						}
					}()
					m.lastF = time.Now()
					m.series = make([]*client.Series, config.MaxSeriesCount)
					copy(scopy, m.series)
				}
				m.Unlock()

			case <-timeout:
				// check if we need to flush
				m.Lock()

				if len(m.series) > 0 && time.Since(m.lastF) > m.config.MaxDuration {
					copy(scopy, m.series)
					go func() {
						err := m.client.WriteSeries(scopy)
						if err != nil {
							log.Println("influxhandler", err)
						}
					}()
					m.lastF = time.Now()
					m.series = make([]*client.Series, config.MaxSeriesCount)
					copy(scopy, m.series)
				}
				m.Unlock()
			}
		}

	}()

	return m
}

// writeSeries saves or bufferizes the series
func (m *Middleware) WriteSeries(s *client.Series) {
	if m.config.MaxDuration == 0 && m.config.MaxSeriesCount == 0 {
		err := m.client.WriteSeries([]*client.Series{s})
		if err != nil {
			log.Println("influxhandler", err)
		}
		return
	}
	m.ch <- s
}

// Handler logs the request as it goes in and the response as it goes out
func (m *Middleware) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if m.client == nil {
			h.ServeHTTP(w, req)
			return
		}
		start := time.Now()
		h.ServeHTTP(w, req)
		t := time.Since(start)
		s := &client.Series{
			Name:    "resp_time",
			Columns: []string{"duration", "code", "url", "method"},
			Points: [][]interface{}{
				// TODO: get the real status code
				[]interface{}{int64(t / time.Millisecond), 200, req.RequestURI, req.Method},
			},
		}
		m.WriteSeries(s)
	})
}
<|endoftext|>


<commit_before>package template

// Functions available to gondola templates

import (
	"encoding/json"
	"fmt"
	"gnd.la/assets"
	"gnd.la/types"
	"html/template"
	"reflect"
	"strconv"
	"strings"
	"time"
)

func eq(args ...interface{}) bool {
	if len(args) == 0 {
		return false
	}
	x := args[0]
	switch x := x.(type) {
	case string, int, int64, byte, float32, float64:
		for _, y := range args[1:] {
			if x == y {
				return true
			}
		}
		return false
	}

	for _, y := range args[1:] {
		if reflect.DeepEqual(x, y) {
			return true
		}
	}
	return false
}

func neq(args ...interface{}) bool {
	return !eq(args...)
}

func lt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() < v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() < v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() < v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func lte(arg1, arg2 interface{}) (bool, error) {
	lessThan, err := lt(arg1, arg2)
	if lessThan || err != nil {
		return lessThan, err
	}
	return eq(arg1, arg2), nil
}

func gt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() > v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() > v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() > v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func gte(arg1, arg2 interface{}) (bool, error) {
	greaterThan, err := gt(arg1, arg2)
	if greaterThan || err != nil {
		return greaterThan, err
	}
	return eq(arg1, arg2), nil
}

func _json(arg interface{}) string {
	if arg == nil {
		return ""
	}
	b, err := json.Marshal(arg)
	if err == nil {
		return string(b)
	}
	return ""
}

func nz(x interface{}) bool {
	switch x := x.(type) {
	case int, uint, int64, uint64, byte, float32, float64:
		return x != 0
	case string:
		return len(x) > 0
	}
	return false
}

func lower(x string) string {
	return strings.ToLower(x)
}

func join(x []string, sep string) string {
	return strings.Join(x, sep)
}

func _map(args ...interface{}) (map[string]interface{}, error) {
	var key string
	m := make(map[string]interface{})
	for ii, v := range args {
		if ii%2 == 0 {
			if s, ok := v.(string); ok {
				key = s
			} else {
				return nil, fmt.Errorf("Invalid argument to map at index %d, %t instead of string", ii, v)
			}
		} else {
			m[key] = v
		}
	}
	return m, nil
}

// this returns *[]interface{} so append works on
// slices declared in templates
func _slice(args ...interface{}) *[]interface{} {
	return &args
}

func _append(items interface{}, args ...interface{}) (string, error) {
	val := reflect.ValueOf(items)
	if !val.IsValid() || val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Slice {
		return "", fmt.Errorf("first argument to append must be pointer to slice, it's %T", items)
	}
	sl := val.Elem()
	for _, v := range args {
		vval := reflect.ValueOf(v)
		if !vval.Type().AssignableTo(sl.Type().Elem()) {
			return "", fmt.Errorf("can't append %s to %s", vval.Type(), sl.Type())
		}
		sl = reflect.Append(sl, vval)
	}
	val.Elem().Set(sl)
	return "", nil
}

func mult(args ...interface{}) (float64, error) {
	val := 1.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val *= float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val *= float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val *= value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("Error parsing string passed to mult at index %d: %s", ii, err)
			}
			val *= v
		default:
			return 0, fmt.Errorf("Invalid argument of type %T passed to mult at index %d", v, ii)
		}
	}
	return val, nil

}

func add(args ...interface{}) (float64, error) {
	val := 0.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val += float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val += float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val += value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("error parsing string passed to add() at index %d: %s", ii, err)
			}
			val += v
		default:
			return 0, fmt.Errorf("invalid argument of type %T passed to add() at index %d", v, ii)
		}
	}
	return val, nil

}

func concat(args ...interface{}) string {
	s := make([]string, len(args))
	for ii, v := range args {
		s[ii] = types.ToString(v)
	}
	return strings.Join(s, "")
}

func and(args ...interface{}) bool {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if !t {
			return false
		}
	}
	return true
}

func or(args ...interface{}) interface{} {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if t {
			return v
		}
	}
	return nil
}

func not(arg interface{}) bool {
	t, _ := types.IsTrue(arg)
	return !t
}

func divisible(n interface{}, d interface{}) (bool, error) {
	ni, err := types.ToInt(n)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid number %v: %s", n, err)
	}
	di, err := types.ToInt(d)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid divisor %v: %s", d, err)
	}
	return ni%di == 0, nil
}

func even(arg interface{}) (bool, error) {
	return divisible(arg, 2)
}

func odd(arg interface{}) (bool, error) {
	res, err := divisible(arg, 2)
	if err != nil {
		return false, err
	}
	return !res, nil
}

func now() time.Time {
	return time.Now()
}

var templateFuncs template.FuncMap = template.FuncMap{
	"eq":        eq,
	"neq":       neq,
	"lt":        lt,
	"lte":       lte,
	"gt":        lt,
	"gte":       lte,
	"json":      _json,
	"nz":        nz,
	"lower":     lower,
	"join":      join,
	"map":       _map,
	"slice":     _slice,
	"append":    _append,
	"mult":      mult,
	"divisible": divisible,
	"add":       add,
	"even":      even,
	"odd":       odd,
	"render":    assets.Render,
	"concat":    concat,
	"and":       and,
	"or":        or,
	"not":       not,
	"now":       now,

	// Go builtins
	"call":     call,
	"html":     template.HTMLEscaper,
	"index":    index,
	"js":       template.JSEscaper,
	"len":      length,
	"print":    fmt.Sprint,
	"printf":   fmt.Sprintf,
	"println":  fmt.Sprintln,
	"urlquery": template.URLQueryEscaper,
}
<commit_msg>Make json template function return a template.JS<commit_after>package template

// Functions available to gondola templates

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gnd.la/assets"
	"gnd.la/mux/serialize"
	"gnd.la/types"
	"html/template"
	"reflect"
	"strconv"
	"strings"
	"time"
)

func eq(args ...interface{}) bool {
	if len(args) == 0 {
		return false
	}
	x := args[0]
	switch x := x.(type) {
	case string, int, int64, byte, float32, float64:
		for _, y := range args[1:] {
			if x == y {
				return true
			}
		}
		return false
	}

	for _, y := range args[1:] {
		if reflect.DeepEqual(x, y) {
			return true
		}
	}
	return false
}

func neq(args ...interface{}) bool {
	return !eq(args...)
}

func lt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() < v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() < v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() < v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func lte(arg1, arg2 interface{}) (bool, error) {
	lessThan, err := lt(arg1, arg2)
	if lessThan || err != nil {
		return lessThan, err
	}
	return eq(arg1, arg2), nil
}

func gt(arg1, arg2 interface{}) (bool, error) {
	v1 := reflect.ValueOf(arg1)
	v2 := reflect.ValueOf(arg2)
	t1 := v1.Type()
	t2 := v2.Type()
	switch {
	case types.IsInt(t1) && types.IsInt(t2):
		return v1.Int() > v2.Int(), nil
	case types.IsUint(t1) && types.IsUint(t2):
		return v1.Uint() > v2.Uint(), nil
	case types.IsFloat(t1) && types.IsFloat(t2):
		return v1.Float() > v2.Float(), nil
	}
	return false, fmt.Errorf("can't compare %T with %T", arg1, arg2)
}

func gte(arg1, arg2 interface{}) (bool, error) {
	greaterThan, err := gt(arg1, arg2)
	if greaterThan || err != nil {
		return greaterThan, err
	}
	return eq(arg1, arg2), nil
}

func jsons(arg interface{}) (string, error) {
	if jw, ok := arg.(serialize.JSONWriter); ok {
		var buf bytes.Buffer
		_, err := jw.WriteJSON(&buf)
		return buf.String(), err
	}
	b, err := json.Marshal(arg)
	return string(b), err
}

func _json(arg interface{}) (template.JS, error) {
	s, err := jsons(arg)
	return template.JS(s), err
}

func nz(x interface{}) bool {
	switch x := x.(type) {
	case int, uint, int64, uint64, byte, float32, float64:
		return x != 0
	case string:
		return len(x) > 0
	}
	return false
}

func lower(x string) string {
	return strings.ToLower(x)
}

func join(x []string, sep string) string {
	return strings.Join(x, sep)
}

func _map(args ...interface{}) (map[string]interface{}, error) {
	var key string
	m := make(map[string]interface{})
	for ii, v := range args {
		if ii%2 == 0 {
			if s, ok := v.(string); ok {
				key = s
			} else {
				return nil, fmt.Errorf("Invalid argument to map at index %d, %t instead of string", ii, v)
			}
		} else {
			m[key] = v
		}
	}
	return m, nil
}

// this returns *[]interface{} so append works on
// slices declared in templates
func _slice(args ...interface{}) *[]interface{} {
	return &args
}

func _append(items interface{}, args ...interface{}) (string, error) {
	val := reflect.ValueOf(items)
	if !val.IsValid() || val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Slice {
		return "", fmt.Errorf("first argument to append must be pointer to slice, it's %T", items)
	}
	sl := val.Elem()
	for _, v := range args {
		vval := reflect.ValueOf(v)
		if !vval.Type().AssignableTo(sl.Type().Elem()) {
			return "", fmt.Errorf("can't append %s to %s", vval.Type(), sl.Type())
		}
		sl = reflect.Append(sl, vval)
	}
	val.Elem().Set(sl)
	return "", nil
}

func mult(args ...interface{}) (float64, error) {
	val := 1.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val *= float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val *= float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val *= value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("Error parsing string passed to mult at index %d: %s", ii, err)
			}
			val *= v
		default:
			return 0, fmt.Errorf("Invalid argument of type %T passed to mult at index %d", v, ii)
		}
	}
	return val, nil

}

func add(args ...interface{}) (float64, error) {
	val := 0.0
	for ii, v := range args {
		value := reflect.ValueOf(v)
		switch value.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val += float64(value.Int())
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			val += float64(value.Uint())
		case reflect.Float32, reflect.Float64:
			val += value.Float()
		case reflect.String:
			v, err := strconv.ParseFloat(value.String(), 64)
			if err != nil {
				return 0, fmt.Errorf("error parsing string passed to add() at index %d: %s", ii, err)
			}
			val += v
		default:
			return 0, fmt.Errorf("invalid argument of type %T passed to add() at index %d", v, ii)
		}
	}
	return val, nil

}

func concat(args ...interface{}) string {
	s := make([]string, len(args))
	for ii, v := range args {
		s[ii] = types.ToString(v)
	}
	return strings.Join(s, "")
}

func and(args ...interface{}) bool {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if !t {
			return false
		}
	}
	return true
}

func or(args ...interface{}) interface{} {
	for _, v := range args {
		t, _ := types.IsTrue(v)
		if t {
			return v
		}
	}
	return nil
}

func not(arg interface{}) bool {
	t, _ := types.IsTrue(arg)
	return !t
}

func divisible(n interface{}, d interface{}) (bool, error) {
	ni, err := types.ToInt(n)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid number %v: %s", n, err)
	}
	di, err := types.ToInt(d)
	if err != nil {
		return false, fmt.Errorf("divisible() invalid divisor %v: %s", d, err)
	}
	return ni%di == 0, nil
}

func even(arg interface{}) (bool, error) {
	return divisible(arg, 2)
}

func odd(arg interface{}) (bool, error) {
	res, err := divisible(arg, 2)
	if err != nil {
		return false, err
	}
	return !res, nil
}

func now() time.Time {
	return time.Now()
}

var templateFuncs template.FuncMap = template.FuncMap{
	"eq":        eq,
	"neq":       neq,
	"lt":        lt,
	"lte":       lte,
	"gt":        lt,
	"gte":       lte,
	"json":      _json,
	"jsons":     jsons,
	"nz":        nz,
	"lower":     lower,
	"join":      join,
	"map":       _map,
	"slice":     _slice,
	"append":    _append,
	"mult":      mult,
	"divisible": divisible,
	"add":       add,
	"even":      even,
	"odd":       odd,
	"render":    assets.Render,
	"concat":    concat,
	"and":       and,
	"or":        or,
	"not":       not,
	"now":       now,

	// Go builtins
	"call":     call,
	"html":     template.HTMLEscaper,
	"index":    index,
	"js":       template.JSEscaper,
	"len":      length,
	"print":    fmt.Sprint,
	"printf":   fmt.Sprintf,
	"println":  fmt.Sprintln,
	"urlquery": template.URLQueryEscaper,
}
<|endoftext|>