<commit_before>package server

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	etcdErr "github.com/coreos/etcd/error"
	"github.com/coreos/etcd/log"
	"github.com/coreos/etcd/mod"
	"github.com/coreos/etcd/server/v1"
	"github.com/coreos/etcd/server/v2"
	"github.com/coreos/etcd/store"
	_ "github.com/coreos/etcd/store/v2"
	"github.com/coreos/raft"
	"github.com/gorilla/mux"
)

// This is the default implementation of the Server interface.
type Server struct {
	http.Server
	peerServer  *PeerServer
	registry    *Registry
	listener    net.Listener
	store       store.Store
	name        string
	url         string
	tlsConf     *TLSConfig
	tlsInfo     *TLSInfo
	router      *mux.Router
	corsHandler *corsHandler
}

// Creates a new Server.
func New(name string, urlStr string, bindAddr string, tlsConf *TLSConfig, tlsInfo *TLSInfo, peerServer *PeerServer, registry *Registry, store store.Store) *Server {
	r := mux.NewRouter()
	cors := &corsHandler{router: r}

	s := &Server{
		Server: http.Server{
			Handler:   cors,
			TLSConfig: &tlsConf.Server,
			Addr:      bindAddr,
		},
		name:        name,
		store:       store,
		registry:    registry,
		url:         urlStr,
		tlsConf:     tlsConf,
		tlsInfo:     tlsInfo,
		peerServer:  peerServer,
		router:      r,
		corsHandler: cors,
	}

	// Install the routes.
	s.handleFunc("/version", s.GetVersionHandler).Methods("GET")
	s.installV1()
	s.installV2()
	s.installMod()

	return s
}

// The current state of the server in the cluster.
func (s *Server) State() string {
	return s.peerServer.RaftServer().State()
}

// The node name of the leader in the cluster.
func (s *Server) Leader() string {
	return s.peerServer.RaftServer().Leader()
}

// The current Raft committed index.
func (s *Server) CommitIndex() uint64 {
	return s.peerServer.RaftServer().CommitIndex()
}

// The current Raft term.
func (s *Server) Term() uint64 {
	return s.peerServer.RaftServer().Term()
}

// The server URL.
func (s *Server) URL() string {
	return s.url
}

// Retrives the Peer URL for a given node name.
func (s *Server) PeerURL(name string) (string, bool) {
	return s.registry.PeerURL(name)
}

// ClientURL retrieves the Client URL for a given node name.
func (s *Server) ClientURL(name string) (string, bool) {
	return s.registry.ClientURL(name)
}

// Returns a reference to the Store.
func (s *Server) Store() store.Store {
	return s.store
}

func (s *Server) installV1() {
	s.handleFuncV1("/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET")
	s.handleFuncV1("/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT")
	s.handleFuncV1("/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE")
	s.handleFuncV1("/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "POST")
	s.handleFunc("/v1/leader", s.GetLeaderHandler).Methods("GET")
	s.handleFunc("/v1/machines", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v1/peers", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v1/stats/self", s.GetStatsHandler).Methods("GET")
	s.handleFunc("/v1/stats/leader", s.GetLeaderStatsHandler).Methods("GET")
	s.handleFunc("/v1/stats/store", s.GetStoreStatsHandler).Methods("GET")
}

func (s *Server) installV2() {
	s.handleFuncV2("/v2/keys/{key:.*}", v2.GetHandler).Methods("GET")
	s.handleFuncV2("/v2/keys/{key:.*}", v2.PostHandler).Methods("POST")
	s.handleFuncV2("/v2/keys/{key:.*}", v2.PutHandler).Methods("PUT")
	s.handleFuncV2("/v2/keys/{key:.*}", v2.DeleteHandler).Methods("DELETE")
	s.handleFunc("/v2/leader", s.GetLeaderHandler).Methods("GET")
	s.handleFunc("/v2/machines", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v2/peers", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v2/stats/self", s.GetStatsHandler).Methods("GET")
	s.handleFunc("/v2/stats/leader", s.GetLeaderStatsHandler).Methods("GET")
	s.handleFunc("/v2/stats/store", s.GetStoreStatsHandler).Methods("GET")
	s.handleFunc("/v2/speedTest", s.SpeedTestHandler).Methods("GET")
}

func (s *Server) installMod() {
	r := s.router
	r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler(s.url)))
}

// Adds a v1 server handler to the router.
func (s *Server) handleFuncV1(path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route {
	return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error {
		return f(w, req, s)
	})
}

// Adds a v2 server handler to the router.
func (s *Server) handleFuncV2(path string, f func(http.ResponseWriter, *http.Request, v2.Server) error) *mux.Route {
	return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error {
		return f(w, req, s)
	})
}

// Adds a server handler to the router.
func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route {
	r := s.router

	// Wrap the standard HandleFunc interface to pass in the server reference.
	return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
		// Log request.
		log.Debugf("[recv] %s %s %s [%s]", req.Method, s.url, req.URL.Path, req.RemoteAddr)

		// Execute handler function and return error if necessary.
		if err := f(w, req); err != nil {
			if etcdErr, ok := err.(*etcdErr.Error); ok {
				log.Debug("Return error: ", (*etcdErr).Error())
				w.Header().Set("Content-Type", "application/json")
				etcdErr.Write(w)
			} else {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	})
}

// Start to listen and response etcd client command
func (s *Server) ListenAndServe() error {
	log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.name, s.Server.Addr, s.url)

	if s.tlsConf.Scheme == "http" {
		return s.listenAndServe()
	} else {
		return s.listenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile)
	}
}

// Overridden version of net/http added so we can manage the listener.
func (s *Server) listenAndServe() error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":http"
	}
	l, e := net.Listen("tcp", addr)
	if e != nil {
		return e
	}
	s.listener = l
	return s.Server.Serve(l)
}

// Overridden version of net/http added so we can manage the listener.
func (s *Server) listenAndServeTLS(certFile, keyFile string) error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{}
	if s.Server.TLSConfig != nil {
		*config = *s.Server.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(conn, config)
	s.listener = tlsListener
	return s.Server.Serve(tlsListener)
}

// Stops the server.
func (s *Server) Close() {
	if s.listener != nil {
		s.listener.Close()
		s.listener = nil
	}
}

// Dispatch command to the current leader
func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error {
	ps := s.peerServer
	if ps.raftServer.State() == raft.Leader {
		result, err := ps.raftServer.Do(c)
		if err != nil {
			return err
		}

		if result == nil {
			return etcdErr.NewError(300, "Empty result from raft", s.Store().Index())
		}

		// response for raft related commands[join/remove]
		if b, ok := result.([]byte); ok {
			w.WriteHeader(http.StatusOK)
			w.Write(b)
			return nil
		}

		var b []byte
		if strings.HasPrefix(req.URL.Path, "/v1") {
			b, _ = json.Marshal(result.(*store.Event).Response(0))
			w.WriteHeader(http.StatusOK)
		} else {
			e, _ := result.(*store.Event)
			b, _ = json.Marshal(e)

			w.Header().Set("Content-Type", "application/json")
			// etcd index should be the same as the event index
			// which is also the last modified index of the node
			w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index))
			w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex()))
			w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term()))

			if e.IsCreated() {
				w.WriteHeader(http.StatusCreated)
			} else {
				w.WriteHeader(http.StatusOK)
			}
		}

		w.Write(b)

		return nil

	} else {
		leader := ps.raftServer.Leader()

		// No leader available.
		if leader == "" {
			return etcdErr.NewError(300, "", s.Store().Index())
		}

		var url string
		switch c.(type) {
		case *JoinCommand, *RemoveCommand:
			url, _ = ps.registry.PeerURL(leader)
		default:
			url, _ = ps.registry.ClientURL(leader)
		}
		redirect(url, w, req)

		return nil
	}
}

// OriginAllowed determines whether the server will allow a given CORS origin.
func (s *Server) OriginAllowed(origin string) bool {
	return s.corsHandler.OriginAllowed(origin)
}

// AllowOrigins sets a comma-delimited list of origins that are allowed.
func (s *Server) AllowOrigins(origins []string) error {
	return s.corsHandler.AllowOrigins(origins)
}

// Handler to return the current version of etcd.
func (s *Server) GetVersionHandler(w http.ResponseWriter, req *http.Request) error {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "etcd %s", ReleaseVersion)
	return nil
}

// Handler to return the current leader's raft address
func (s *Server) GetLeaderHandler(w http.ResponseWriter, req *http.Request) error {
	leader := s.peerServer.RaftServer().Leader()
	if leader == "" {
		return etcdErr.NewError(etcdErr.EcodeLeaderElect, "", s.Store().Index())
	}
	w.WriteHeader(http.StatusOK)
	url, _ := s.registry.PeerURL(leader)
	w.Write([]byte(url))
	return nil
}

// Handler to return all the known peers in the current cluster.
func (s *Server) GetPeersHandler(w http.ResponseWriter, req *http.Request) error {
	peers := s.registry.ClientURLs(s.peerServer.RaftServer().Leader(), s.name)
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(strings.Join(peers, ", ")))
	return nil
}

// Retrieves stats on the Raft server.
func (s *Server) GetStatsHandler(w http.ResponseWriter, req *http.Request) error {
	w.Write(s.peerServer.Stats())
	return nil
}

// Retrieves stats on the leader.
func (s *Server) GetLeaderStatsHandler(w http.ResponseWriter, req *http.Request) error {
	if s.peerServer.RaftServer().State() == raft.Leader {
		w.Write(s.peerServer.PeerStats())
		return nil
	}

	leader := s.peerServer.RaftServer().Leader()
	if leader == "" {
		return etcdErr.NewError(300, "", s.Store().Index())
	}
	hostname, _ := s.registry.ClientURL(leader)
	redirect(hostname, w, req)
	return nil
}

// Retrieves stats on the leader.
func (s *Server) GetStoreStatsHandler(w http.ResponseWriter, req *http.Request) error {
	w.Write(s.store.JsonStats())
	return nil
}

// Executes a speed test to evaluate the performance of update replication.
func (s *Server) SpeedTestHandler(w http.ResponseWriter, req *http.Request) error {
	count := 1000
	c := make(chan bool, count)
	for i := 0; i < count; i++ {
		go func() {
			for j := 0; j < 10; j++ {
				c := s.Store().CommandFactory().CreateSetCommand("foo", false, "bar", time.Unix(0, 0))
				s.peerServer.RaftServer().Do(c)
			}
			c <- true
		}()
	}

	for i := 0; i < count; i++ {
		<-c
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("speed test success"))
	return nil
}
<commit_msg>fix(dispatch) should call e.Index()<commit_after>package server

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	etcdErr "github.com/coreos/etcd/error"
	"github.com/coreos/etcd/log"
	"github.com/coreos/etcd/mod"
	"github.com/coreos/etcd/server/v1"
	"github.com/coreos/etcd/server/v2"
	"github.com/coreos/etcd/store"
	_ "github.com/coreos/etcd/store/v2"
	"github.com/coreos/raft"
	"github.com/gorilla/mux"
)

// This is the default implementation of the Server interface.
type Server struct {
	http.Server
	peerServer  *PeerServer
	registry    *Registry
	listener    net.Listener
	store       store.Store
	name        string
	url         string
	tlsConf     *TLSConfig
	tlsInfo     *TLSInfo
	router      *mux.Router
	corsHandler *corsHandler
}

// Creates a new Server.
func New(name string, urlStr string, bindAddr string, tlsConf *TLSConfig, tlsInfo *TLSInfo, peerServer *PeerServer, registry *Registry, store store.Store) *Server {
	r := mux.NewRouter()
	cors := &corsHandler{router: r}

	s := &Server{
		Server: http.Server{
			Handler:   cors,
			TLSConfig: &tlsConf.Server,
			Addr:      bindAddr,
		},
		name:        name,
		store:       store,
		registry:    registry,
		url:         urlStr,
		tlsConf:     tlsConf,
		tlsInfo:     tlsInfo,
		peerServer:  peerServer,
		router:      r,
		corsHandler: cors,
	}

	// Install the routes.
	s.handleFunc("/version", s.GetVersionHandler).Methods("GET")
	s.installV1()
	s.installV2()
	s.installMod()

	return s
}

// The current state of the server in the cluster.
func (s *Server) State() string {
	return s.peerServer.RaftServer().State()
}

// The node name of the leader in the cluster.
func (s *Server) Leader() string {
	return s.peerServer.RaftServer().Leader()
}

// The current Raft committed index.
func (s *Server) CommitIndex() uint64 {
	return s.peerServer.RaftServer().CommitIndex()
}

// The current Raft term.
func (s *Server) Term() uint64 {
	return s.peerServer.RaftServer().Term()
}

// The server URL.
func (s *Server) URL() string {
	return s.url
}

// Retrives the Peer URL for a given node name.
func (s *Server) PeerURL(name string) (string, bool) {
	return s.registry.PeerURL(name)
}

// ClientURL retrieves the Client URL for a given node name.
func (s *Server) ClientURL(name string) (string, bool) {
	return s.registry.ClientURL(name)
}

// Returns a reference to the Store.
func (s *Server) Store() store.Store {
	return s.store
}

func (s *Server) installV1() {
	s.handleFuncV1("/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET")
	s.handleFuncV1("/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT")
	s.handleFuncV1("/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE")
	s.handleFuncV1("/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "POST")
	s.handleFunc("/v1/leader", s.GetLeaderHandler).Methods("GET")
	s.handleFunc("/v1/machines", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v1/peers", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v1/stats/self", s.GetStatsHandler).Methods("GET")
	s.handleFunc("/v1/stats/leader", s.GetLeaderStatsHandler).Methods("GET")
	s.handleFunc("/v1/stats/store", s.GetStoreStatsHandler).Methods("GET")
}

func (s *Server) installV2() {
	s.handleFuncV2("/v2/keys/{key:.*}", v2.GetHandler).Methods("GET")
	s.handleFuncV2("/v2/keys/{key:.*}", v2.PostHandler).Methods("POST")
	s.handleFuncV2("/v2/keys/{key:.*}", v2.PutHandler).Methods("PUT")
	s.handleFuncV2("/v2/keys/{key:.*}", v2.DeleteHandler).Methods("DELETE")
	s.handleFunc("/v2/leader", s.GetLeaderHandler).Methods("GET")
	s.handleFunc("/v2/machines", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v2/peers", s.GetPeersHandler).Methods("GET")
	s.handleFunc("/v2/stats/self", s.GetStatsHandler).Methods("GET")
	s.handleFunc("/v2/stats/leader", s.GetLeaderStatsHandler).Methods("GET")
	s.handleFunc("/v2/stats/store", s.GetStoreStatsHandler).Methods("GET")
	s.handleFunc("/v2/speedTest", s.SpeedTestHandler).Methods("GET")
}

func (s *Server) installMod() {
	r := s.router
	r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler(s.url)))
}

// Adds a v1 server handler to the router.
func (s *Server) handleFuncV1(path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route {
	return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error {
		return f(w, req, s)
	})
}

// Adds a v2 server handler to the router.
func (s *Server) handleFuncV2(path string, f func(http.ResponseWriter, *http.Request, v2.Server) error) *mux.Route {
	return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error {
		return f(w, req, s)
	})
}

// Adds a server handler to the router.
func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route {
	r := s.router

	// Wrap the standard HandleFunc interface to pass in the server reference.
	return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
		// Log request.
		log.Debugf("[recv] %s %s %s [%s]", req.Method, s.url, req.URL.Path, req.RemoteAddr)

		// Execute handler function and return error if necessary.
		if err := f(w, req); err != nil {
			if etcdErr, ok := err.(*etcdErr.Error); ok {
				log.Debug("Return error: ", (*etcdErr).Error())
				w.Header().Set("Content-Type", "application/json")
				etcdErr.Write(w)
			} else {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	})
}

// Start to listen and response etcd client command
func (s *Server) ListenAndServe() error {
	log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.name, s.Server.Addr, s.url)

	if s.tlsConf.Scheme == "http" {
		return s.listenAndServe()
	} else {
		return s.listenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile)
	}
}

// Overridden version of net/http added so we can manage the listener.
func (s *Server) listenAndServe() error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":http"
	}
	l, e := net.Listen("tcp", addr)
	if e != nil {
		return e
	}
	s.listener = l
	return s.Server.Serve(l)
}

// Overridden version of net/http added so we can manage the listener.
func (s *Server) listenAndServeTLS(certFile, keyFile string) error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{}
	if s.Server.TLSConfig != nil {
		*config = *s.Server.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(conn, config)
	s.listener = tlsListener
	return s.Server.Serve(tlsListener)
}

// Stops the server.
func (s *Server) Close() {
	if s.listener != nil {
		s.listener.Close()
		s.listener = nil
	}
}

// Dispatch command to the current leader
func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error {
	ps := s.peerServer
	if ps.raftServer.State() == raft.Leader {
		result, err := ps.raftServer.Do(c)
		if err != nil {
			return err
		}

		if result == nil {
			return etcdErr.NewError(300, "Empty result from raft", s.Store().Index())
		}

		// response for raft related commands[join/remove]
		if b, ok := result.([]byte); ok {
			w.WriteHeader(http.StatusOK)
			w.Write(b)
			return nil
		}

		var b []byte
		if strings.HasPrefix(req.URL.Path, "/v1") {
			b, _ = json.Marshal(result.(*store.Event).Response(0))
			w.WriteHeader(http.StatusOK)
		} else {
			e, _ := result.(*store.Event)
			b, _ = json.Marshal(e)

			w.Header().Set("Content-Type", "application/json")
			// etcd index should be the same as the event index
			// which is also the last modified index of the node
			w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index()))
			w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex()))
			w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term()))

			if e.IsCreated() {
				w.WriteHeader(http.StatusCreated)
			} else {
				w.WriteHeader(http.StatusOK)
			}
		}

		w.Write(b)

		return nil

	} else {
		leader := ps.raftServer.Leader()

		// No leader available.
		if leader == "" {
			return etcdErr.NewError(300, "", s.Store().Index())
		}

		var url string
		switch c.(type) {
		case *JoinCommand, *RemoveCommand:
			url, _ = ps.registry.PeerURL(leader)
		default:
			url, _ = ps.registry.ClientURL(leader)
		}
		redirect(url, w, req)

		return nil
	}
}

// OriginAllowed determines whether the server will allow a given CORS origin.
func (s *Server) OriginAllowed(origin string) bool {
	return s.corsHandler.OriginAllowed(origin)
}

// AllowOrigins sets a comma-delimited list of origins that are allowed.
func (s *Server) AllowOrigins(origins []string) error {
	return s.corsHandler.AllowOrigins(origins)
}

// Handler to return the current version of etcd.
func (s *Server) GetVersionHandler(w http.ResponseWriter, req *http.Request) error {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "etcd %s", ReleaseVersion)
	return nil
}

// Handler to return the current leader's raft address
func (s *Server) GetLeaderHandler(w http.ResponseWriter, req *http.Request) error {
	leader := s.peerServer.RaftServer().Leader()
	if leader == "" {
		return etcdErr.NewError(etcdErr.EcodeLeaderElect, "", s.Store().Index())
	}
	w.WriteHeader(http.StatusOK)
	url, _ := s.registry.PeerURL(leader)
	w.Write([]byte(url))
	return nil
}

// Handler to return all the known peers in the current cluster.
func (s *Server) GetPeersHandler(w http.ResponseWriter, req *http.Request) error {
	peers := s.registry.ClientURLs(s.peerServer.RaftServer().Leader(), s.name)
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(strings.Join(peers, ", ")))
	return nil
}

// Retrieves stats on the Raft server.
func (s *Server) GetStatsHandler(w http.ResponseWriter, req *http.Request) error {
	w.Write(s.peerServer.Stats())
	return nil
}

// Retrieves stats on the leader.
func (s *Server) GetLeaderStatsHandler(w http.ResponseWriter, req *http.Request) error {
	if s.peerServer.RaftServer().State() == raft.Leader {
		w.Write(s.peerServer.PeerStats())
		return nil
	}

	leader := s.peerServer.RaftServer().Leader()
	if leader == "" {
		return etcdErr.NewError(300, "", s.Store().Index())
	}
	hostname, _ := s.registry.ClientURL(leader)
	redirect(hostname, w, req)
	return nil
}

// Retrieves stats on the leader.
func (s *Server) GetStoreStatsHandler(w http.ResponseWriter, req *http.Request) error {
	w.Write(s.store.JsonStats())
	return nil
}

// Executes a speed test to evaluate the performance of update replication.
func (s *Server) SpeedTestHandler(w http.ResponseWriter, req *http.Request) error {
	count := 1000
	c := make(chan bool, count)
	for i := 0; i < count; i++ {
		go func() {
			for j := 0; j < 10; j++ {
				c := s.Store().CommandFactory().CreateSetCommand("foo", false, "bar", time.Unix(0, 0))
				s.peerServer.RaftServer().Do(c)
			}
			c <- true
		}()
	}

	for i := 0; i < count; i++ {
		<-c
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("speed test success"))
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/thomasjpfan/docker-scaler/service"
)

// Server runs service that scales docker services
type Server struct {
	scaler  service.ScalerServicer
	alerter service.AlertServicer
	logger  *log.Logger
}

// NewServer creates Server
func NewServer(
	scaler service.ScalerServicer,
	alerter service.AlertServicer,
	logger *log.Logger) *Server {
	return &Server{
		scaler:  scaler,
		alerter: alerter,
		logger:  logger,
	}
}

// MakeRouter routes url paths to handlers
func (s *Server) MakeRouter() *mux.Router {
	m := mux.NewRouter()
	m.Path("/scale").
		Queries("service", "{service}", "delta", "{delta}").
		Methods("POST").
		HandlerFunc(s.ScaleService).
		Name("ScaleService")
	return m
}

// Run starts server
func (s *Server) Run(port uint16) {
	address := fmt.Sprintf(":%d", port)
	m := s.MakeRouter()
	log.Fatal(http.ListenAndServe(address, m))
}

// ScaleService scales service
func (s *Server) ScaleService(w http.ResponseWriter, r *http.Request) {

	q := r.URL.Query()
	serviceID := q.Get("service")
	deltaStr := q.Get("delta")
	delta, err := strconv.Atoi(deltaStr)

	requestMessage := fmt.Sprintf("Scale service: %s, delta: %s", serviceID, deltaStr)
	s.logger.Printf(requestMessage)

	if err != nil {
		message := fmt.Sprintf("Incorrect delta query: %v", deltaStr)
		respondWithError(w, http.StatusBadRequest, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}

	minReplicas, maxReplicas, err := s.scaler.GetMinMaxReplicas(serviceID)
	if err != nil {
		message := err.Error()
		respondWithError(w, http.StatusInternalServerError, message)
		s.logger.Print(message)
		err := s.alerter.Send("scale_service", serviceID, requestMessage, "error", message)
		if err != nil {
			s.logger.Print(err.Error())
		}
		return
	}

	replicas, err := s.scaler.GetReplicas(serviceID)
	if err != nil {
		message := err.Error()
		respondWithError(w, http.StatusInternalServerError, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}
	newReplicasInt := int(replicas) + delta

	if newReplicasInt <= 0 {
		message := fmt.Sprintf("Delta %d results in a negative number of replicas for service: %s", delta, serviceID)
		respondWithError(w, http.StatusBadRequest, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}

	newReplicas := uint64(newReplicasInt)
	if newReplicas > maxReplicas {
		message := fmt.Sprintf("%s is already scaled to the maximum number of %d replicas", serviceID, maxReplicas)
		respondWithError(w, http.StatusPreconditionFailed, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	} else if newReplicas < minReplicas {
		message := fmt.Sprintf("%s is already descaled to the minimum number of %d replicas", serviceID, minReplicas)
		respondWithError(w, http.StatusPreconditionFailed, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}

	err = s.scaler.SetReplicas(serviceID, newReplicas)
	if err != nil {
		message := err.Error()
		respondWithError(w, http.StatusInternalServerError, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}
	message := fmt.Sprintf("Scaling %s to %d replicas", serviceID, newReplicas)
	s.sendMessage("scale_service", serviceID, requestMessage, "success", message)
	respondWithJSON(w, http.StatusOK, Response{Status: "OK", Message: message})
}

func (s *Server) sendMessage(alertName string, serviceName string, request string,
	status string, message string) {
	s.logger.Print(message)
	err := s.alerter.Send(alertName, serviceName, request, status, message)
	if err != nil {
		s.logger.Print(err.Error())
	}
}
<commit_msg>ENH: Logs sending alerts to alertmanager<commit_after>package server

import (
	"fmt"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/thomasjpfan/docker-scaler/service"
)

// Server runs service that scales docker services
type Server struct {
	scaler  service.ScalerServicer
	alerter service.AlertServicer
	logger  *log.Logger
}

// NewServer creates Server
func NewServer(
	scaler service.ScalerServicer,
	alerter service.AlertServicer,
	logger *log.Logger) *Server {
	return &Server{
		scaler:  scaler,
		alerter: alerter,
		logger:  logger,
	}
}

// MakeRouter routes url paths to handlers
func (s *Server) MakeRouter() *mux.Router {
	m := mux.NewRouter()
	m.Path("/scale").
		Queries("service", "{service}", "delta", "{delta}").
		Methods("POST").
		HandlerFunc(s.ScaleService).
		Name("ScaleService")
	return m
}

// Run starts server
func (s *Server) Run(port uint16) {
	address := fmt.Sprintf(":%d", port)
	m := s.MakeRouter()
	log.Fatal(http.ListenAndServe(address, m))
}

// ScaleService scales service
func (s *Server) ScaleService(w http.ResponseWriter, r *http.Request) {

	q := r.URL.Query()
	serviceID := q.Get("service")
	deltaStr := q.Get("delta")
	delta, err := strconv.Atoi(deltaStr)

	requestMessage := fmt.Sprintf("Scale service: %s, delta: %s", serviceID, deltaStr)
	s.logger.Printf(requestMessage)

	if err != nil {
		message := fmt.Sprintf("Incorrect delta query: %v", deltaStr)
		respondWithError(w, http.StatusBadRequest, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}

	minReplicas, maxReplicas, err := s.scaler.GetMinMaxReplicas(serviceID)
	if err != nil {
		message := err.Error()
		respondWithError(w, http.StatusInternalServerError, message)
		s.logger.Print(message)
		err := s.alerter.Send("scale_service", serviceID, requestMessage, "error", message)
		if err != nil {
			s.logger.Print(err.Error())
		}
		return
	}

	replicas, err := s.scaler.GetReplicas(serviceID)
	if err != nil {
		message := err.Error()
		respondWithError(w, http.StatusInternalServerError, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}
	newReplicasInt := int(replicas) + delta

	if newReplicasInt <= 0 {
		message := fmt.Sprintf("Delta %d results in a negative number of replicas for service: %s", delta, serviceID)
		respondWithError(w, http.StatusBadRequest, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}

	newReplicas := uint64(newReplicasInt)
	if newReplicas > maxReplicas {
		message := fmt.Sprintf("%s is already scaled to the maximum number of %d replicas", serviceID, maxReplicas)
		respondWithError(w, http.StatusPreconditionFailed, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	} else if newReplicas < minReplicas {
		message := fmt.Sprintf("%s is already descaled to the minimum number of %d replicas", serviceID, minReplicas)
		respondWithError(w, http.StatusPreconditionFailed, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}

	err = s.scaler.SetReplicas(serviceID, newReplicas)
	if err != nil {
		message := err.Error()
		respondWithError(w, http.StatusInternalServerError, message)
		s.sendMessage("scale_service", serviceID, requestMessage, "error", message)
		return
	}
	message := fmt.Sprintf("Scaling %s to %d replicas", serviceID, newReplicas)
	s.sendMessage("scale_service", serviceID, requestMessage, "success", message)
	respondWithJSON(w, http.StatusOK, Response{Status: "OK", Message: message})
}

func (s *Server) sendMessage(alertName string, serviceName string, request string,
	status string, message string) {
	s.logger.Print(message)
	err := s.alerter.Send(alertName, serviceName, request, status, message)
	if err != nil {
		s.logger.Printf("Alertmanager did not receive message: %s, error: %s", message, err.Error())
	} else {
		s.logger.Printf("Alertmanager received message: %s", message)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/client"
	"github.com/micro/go-micro/v2/cmd"
	log "github.com/micro/go-micro/v2/logger"
	gorun "github.com/micro/go-micro/v2/runtime"
	"github.com/micro/go-micro/v2/util/file"
	ccli "github.com/micro/micro/v2/client/cli"
	"github.com/micro/micro/v2/internal/update"
)

var (
	// list of services managed
	services = []string{
		// runtime services
		"config",   // ????
		"auth",     // :8010
		"network",  // :8085
		"runtime",  // :8088
		"registry", // :8000
		"broker",   // :8001
		"store",    // :8002
		"debug",    // :????
		"proxy",    // :8081
		"api",      // :8080
		"web",      // :8082
	}
)

var (
	// Name of the server microservice
	Name = "go.micro.server"
	// Address is the router microservice bind address
	Address = ":10001"
)

// upload is used for file uploads to the server
func upload(ctx *cli.Context, args []string) ([]byte, error) {
	if ctx.Args().Len() == 0 {
		return nil, errors.New("Required filename to upload")
	}

	filename := ctx.Args().Get(0)
	localfile := ctx.Args().Get(1)

	fileClient := file.New("go.micro.server", client.DefaultClient)
	return nil, fileClient.Upload(filename, localfile)
}

func Commands(options ...micro.Option) []*cli.Command {
	command := &cli.Command{
		Name:  "server",
		Usage: "Run the micro server",
		Description: `Launching the micro server ('micro server') will enable one to connect to it by
		setting the appropriate Micro environment (see 'micro env' && 'micro env --help') commands.`,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "address",
				Usage:   "Set the micro server address :10001",
				EnvVars: []string{"MICRO_SERVER_ADDRESS"},
			},
			&cli.BoolFlag{
				Name:  "peer",
				Usage: "Peer with the global network to share services",
			},
			&cli.StringFlag{
				Name:    "profile",
				Usage:   "Set the runtime profile to use for services e.g local, kubernetes, platform",
				EnvVars: []string{"MICRO_RUNTIME_PROFILE"},
			},
		},
		Action: func(ctx *cli.Context) error {
			Run(ctx)
			return nil
		},
		Subcommands: []*cli.Command{{
			Name:  "file",
			Usage: "Move files between your local machine and the server",
			Subcommands: []*cli.Command{
				{
					Name:   "upload",
					Action: ccli.Print(upload),
				},
			},
		}},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []*cli.Command{command}
}

// Run runs the entire platform
func Run(context *cli.Context) error {
	log.Init(log.WithFields(map[string]interface{}{"service": "micro"}))

	if context.Args().Len() > 0 {
		cli.ShowSubcommandHelp(context)
		os.Exit(1)
	}

	// get the network flag
	peer := context.Bool("peer")

	// pass through the environment
	// By default we want a file store when we run micro server.
	// This will get overridden if user has set their own MICRO_STORE env var or passed in --store
	env := []string{"MICRO_STORE=file"}
	profile := context.String("profile")
	if len(profile) == 0 {
		profile = "server"
	}

	env = append(env, "MICRO_RUNTIME_PROFILE="+profile)
	env = append(env, os.Environ()...)

	// connect to the network if specified
	if peer {
		log.Info("Setting global network")

		if v := os.Getenv("MICRO_NETWORK_NODES"); len(v) == 0 {
			// set the resolver to use https://micro.mu/network
			env = append(env, "MICRO_NETWORK_NODES=network.micro.mu")
			log.Info("Setting default network micro.mu")
		}
		if v := os.Getenv("MICRO_NETWORK_TOKEN"); len(v) == 0 {
			// set the network token
			env = append(env, "MICRO_NETWORK_TOKEN=micro.mu")
			log.Info("Setting default network token")
		}
	}

	log.Info("Loading core services")

	// create new micro runtime
	muRuntime := cmd.DefaultCmd.Options().Runtime

	// Use default update notifier
	if context.Bool("auto_update") {
		updateURL := context.String("update_url")
		if len(updateURL) == 0 {
			updateURL = update.DefaultURL
		}

		options := []gorun.Option{
			gorun.WithScheduler(update.NewScheduler(updateURL, fmt.Sprintf("%d", time.Now().Unix()))),
		}
		(*muRuntime).Init(options...)
	}

	for _, service := range services {
		name := service

		if namespace := context.String("namespace"); len(namespace) > 0 {
			name = fmt.Sprintf("%s.%s", namespace, service)
		}

		log.Infof("Registering %s", name)
		// @todo this is a hack
		envs := env
		switch service {
		case "proxy", "web", "api":
			envs = append(envs, "MICRO_AUTH=service")
			envs = append(envs, "MICRO_REGISTRY=service")
		}

		cmdArgs := []string{}
		// we want to pass through the global args so go up one level in the context lineage
		if len(context.Lineage()) > 1 {
			globCtx := context.Lineage()[1]
			for _, f := range globCtx.FlagNames() {
				cmdArgs = append(cmdArgs, "--"+f, context.String(f))
			}
		}
		cmdArgs = append(cmdArgs, service)

		// runtime based on environment we run the service in
		args := []gorun.CreateOption{
			gorun.WithCommand(os.Args[0]),
			gorun.WithArgs(cmdArgs...),
			gorun.WithEnv(envs),
			gorun.WithOutput(os.Stdout),
			gorun.WithRetries(10),
			gorun.CreateImage("micro/micro"),
		}

		// NOTE: we use Version right now to check for the latest release
		muService := &gorun.Service{Name: name, Version: fmt.Sprintf("%d", time.Now().Unix())}
		if err := (*muRuntime).Create(muService, args...); err != nil {
			log.Errorf("Failed to create runtime environment: %v", err)
			return err
		}
	}

	log.Info("Starting service runtime")

	// start the runtime
	if err := (*muRuntime).Start(); err != nil {
		log.Fatal(err)
		return err
	}

	log.Info("Service runtime started")

	// TODO: should we launch the console?
	// start the console
	// cli.Init(context)

	server := micro.NewService(
		micro.Name(Name),
		micro.Address(Address),
	)

	// @todo make this configurable
	uploadDir := filepath.Join(os.TempDir(), "micro", "uploads")
	os.MkdirAll(uploadDir, 0777)
	file.RegisterHandler(server.Server(), uploadDir)
	// start the server
	server.Run()

	log.Info("Stopping service runtime")

	// stop all the things
	if err := (*muRuntime).Stop(); err != nil {
		log.Fatal(err)
		return err
	}

	log.Info("Service runtime shutdown")

	// exit success
	os.Exit(0)
	return nil
}
<commit_msg>fix server startup commands<commit_after>package server

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/client"
	"github.com/micro/go-micro/v2/cmd"
	log "github.com/micro/go-micro/v2/logger"
	gorun "github.com/micro/go-micro/v2/runtime"
	"github.com/micro/go-micro/v2/util/file"
	ccli "github.com/micro/micro/v2/client/cli"
	"github.com/micro/micro/v2/internal/update"
)

var (
	// list of services managed
	services = []string{
		// runtime services
		"config",   // ????
		"auth",     // :8010
		"network",  // :8085
		"runtime",  // :8088
		"registry", // :8000
		"broker",   // :8001
		"store",    // :8002
		"debug",    // :????
		"proxy",    // :8081
		"api",      // :8080
		"web",      // :8082
	}
)

var (
	// Name of the server microservice
	Name = "go.micro.server"
	// Address is the router microservice bind address
	Address = ":10001"
)

// upload is used for file uploads to the server
func upload(ctx *cli.Context, args []string) ([]byte, error) {
	if ctx.Args().Len() == 0 {
		return nil, errors.New("Required filename to upload")
	}

	filename := ctx.Args().Get(0)
	localfile := ctx.Args().Get(1)

	fileClient := file.New("go.micro.server", client.DefaultClient)
	return nil, fileClient.Upload(filename, localfile)
}

func Commands(options ...micro.Option) []*cli.Command {
	command := &cli.Command{
		Name:  "server",
		Usage: "Run the micro server",
		Description: `Launching the micro server ('micro server') will enable one to connect to it by
		setting the appropriate Micro environment (see 'micro env' && 'micro env --help') commands.`,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "address",
				Usage:   "Set the micro server address :10001",
				EnvVars: []string{"MICRO_SERVER_ADDRESS"},
			},
			&cli.BoolFlag{
				Name:  "peer",
				Usage: "Peer with the global network to share services",
			},
			&cli.StringFlag{
				Name:    "profile",
				Usage:   "Set the runtime profile to use for services e.g local, kubernetes, platform",
				EnvVars: []string{"MICRO_RUNTIME_PROFILE"},
			},
		},
		Action: func(ctx *cli.Context) error {
			Run(ctx)
			return nil
		},
		Subcommands: []*cli.Command{{
			Name:  "file",
			Usage: "Move files between your local machine and the server",
			Subcommands: []*cli.Command{
				{
					Name:   "upload",
					Action: ccli.Print(upload),
				},
			},
		}},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []*cli.Command{command}
}

// Run runs the entire platform
func Run(context *cli.Context) error {
	log.Init(log.WithFields(map[string]interface{}{"service": "micro"}))

	if context.Args().Len() > 0 {
		cli.ShowSubcommandHelp(context)
		os.Exit(1)
	}

	// get the network flag
	peer := context.Bool("peer")

	// pass through the environment
	// By default we want a file store when we run micro server.
	// This will get overridden if user has set their own MICRO_STORE env var or passed in --store
	env := []string{"MICRO_STORE=file"}
	profile := context.String("profile")
	if len(profile) == 0 {
		profile = "server"
	}

	env = append(env, "MICRO_RUNTIME_PROFILE="+profile)
	env = append(env, os.Environ()...)

	// connect to the network if specified
	if peer {
		log.Info("Setting global network")

		if v := os.Getenv("MICRO_NETWORK_NODES"); len(v) == 0 {
			// set the resolver to use https://micro.mu/network
			env = append(env, "MICRO_NETWORK_NODES=network.micro.mu")
			log.Info("Setting default network micro.mu")
		}
		if v := os.Getenv("MICRO_NETWORK_TOKEN"); len(v) == 0 {
			// set the network token
			env = append(env, "MICRO_NETWORK_TOKEN=micro.mu")
			log.Info("Setting default network token")
		}
	}

	log.Info("Loading core services")

	// create new micro runtime
	muRuntime := cmd.DefaultCmd.Options().Runtime

	// Use default update notifier
	if context.Bool("auto_update") {
		updateURL := context.String("update_url")
		if len(updateURL) == 0 {
			updateURL = update.DefaultURL
		}

		options := []gorun.Option{
			gorun.WithScheduler(update.NewScheduler(updateURL, fmt.Sprintf("%d", time.Now().Unix()))),
		}
		(*muRuntime).Init(options...)
	}

	for _, service := range services {
		name := service

		if namespace := context.String("namespace"); len(namespace) > 0 {
			name = fmt.Sprintf("%s.%s", namespace, service)
		}

		log.Infof("Registering %s", name)
		// @todo this is a hack
		envs := env
		var isClient bool
		switch service {
		case "proxy", "web", "api", "bot", "cli":
			isClient = true
			envs = append(envs, "MICRO_AUTH=service")
			envs = append(envs, "MICRO_REGISTRY=service")
		}

		cmdArgs := []string{}
		if !isClient {
			// run non-clients as "micro service [name]"
			cmdArgs = append(cmdArgs, "service")
		}

		// we want to pass through the global args so go up one level in the context lineage
		if len(context.Lineage()) > 1 {
			globCtx := context.Lineage()[1]
			for _, f := range globCtx.FlagNames() {
				cmdArgs = append(cmdArgs, "--"+f, context.String(f))
			}
		}
		cmdArgs = append(cmdArgs, service)

		// runtime based on environment we run the service in
		args := []gorun.CreateOption{
			gorun.WithCommand(os.Args[0]),
			gorun.WithArgs(cmdArgs...),
			gorun.WithEnv(envs),
			gorun.WithOutput(os.Stdout),
			gorun.WithRetries(10),
			gorun.CreateImage("micro/micro"),
		}

		// NOTE: we use Version right now to check for the latest release
		muService := &gorun.Service{Name: name, Version: fmt.Sprintf("%d", time.Now().Unix())}
		if err := (*muRuntime).Create(muService, args...); err != nil {
			log.Errorf("Failed to create runtime environment: %v", err)
			return err
		}
	}

	log.Info("Starting service runtime")

	// start the runtime
	if err := (*muRuntime).Start(); err != nil {
		log.Fatal(err)
		return err
	}

	log.Info("Service runtime started")

	// TODO: should we launch the console?
	// start the console
	// cli.Init(context)

	server := micro.NewService(
		micro.Name(Name),
		micro.Address(Address),
	)

	// @todo make this configurable
	uploadDir := filepath.Join(os.TempDir(), "micro", "uploads")
	os.MkdirAll(uploadDir, 0777)
	file.RegisterHandler(server.Server(), uploadDir)
	// start the server
	server.Run()

	log.Info("Stopping service runtime")

	// stop all the things
	if err := (*muRuntime).Stop(); err != nil {
		log.Fatal(err)
		return err
	}

	log.Info("Service runtime shutdown")

	// exit success
	os.Exit(0)
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"io"
	"log"
	"net"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	"github.com/vito/garden/backend"
	"github.com/vito/garden/messagereader"
	protocol "github.com/vito/garden/protocol"
)

type WardenServer struct {
	socketPath string
	backend    backend.Backend
}

func New(socketPath string, backend backend.Backend) *WardenServer {
	return &WardenServer{
		socketPath: socketPath,
		backend:    backend,
	}
}

func (s *WardenServer) Start() error {
	listener, err := net.Listen("unix", s.socketPath)
	if err != nil {
		return err
	}

	go s.handleConnections(listener)

	return nil
}

func (s *WardenServer) handleConnections(listener net.Listener) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println("error accepting connection:", err)
			continue
		}

		go s.serveConnection(conn)
	}
}

func (s *WardenServer) serveConnection(conn net.Conn) {
	for {
		request, err := messagereader.ReadRequest(conn)
		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error reading request:", err)
			continue
		}

		var response proto.Message

		switch request.(type) {
		case *protocol.PingRequest:
			response = &protocol.PingResponse{}
		case *protocol.EchoRequest:
			response = &protocol.EchoResponse{
				Message: request.(*protocol.EchoRequest).Message,
			}
		case *protocol.CreateRequest:
			createRequest := request.(*protocol.CreateRequest)

			bindMounts := []backend.BindMount{}

			for _, bm := range createRequest.GetBindMounts() {
				bindMount := backend.BindMount{
					SrcPath: bm.GetSrcPath(),
					DstPath: bm.GetDstPath(),
					Mode:    backend.BindMountMode(bm.GetMode()),
				}

				bindMounts = append(bindMounts, bindMount)
			}

			container, err := s.backend.Create(backend.ContainerSpec{
				Handle:     createRequest.GetHandle(),
				GraceTime:  time.Duration(createRequest.GetGraceTime()) * time.Second,
				RootFSPath: createRequest.GetRootfs(),
				Network:    createRequest.GetNetwork(),
				BindMounts: bindMounts,
			})

			if err != nil {
				response = errorResponse(err)
				break
			}

			response = &protocol.CreateResponse{
				Handle: proto.String(container.Handle()),
			}
		case *protocol.DestroyRequest:
			handle := request.(*protocol.DestroyRequest).GetHandle()

			err := s.backend.Destroy(handle)
			if err != nil {
				response = errorResponse(err)
				break
			}

			response = &protocol.DestroyResponse{}
		case *protocol.ListRequest:
			containers, err := s.backend.Containers()
			if err != nil {
				response = errorResponse(err)
				break
			}

			handles := []string{}

			for _, container := range containers {
				handles = append(handles, container.Handle())
			}

			response = &protocol.ListResponse{
				Handles: handles,
			}
		case *protocol.CopyInRequest:
			copyIn := request.(*protocol.CopyInRequest)

			handle := copyIn.GetHandle()
			srcPath := copyIn.GetSrcPath()
			dstPath := copyIn.GetDstPath()

			container, err := s.backend.Lookup(handle)
			if err != nil {
				response = errorResponse(err)
				break
			}

			err = container.CopyIn(srcPath, dstPath)
			if err != nil {
				response = errorResponse(err)
				break
			}

			response = &protocol.CopyInResponse{}
		case *protocol.CopyOutRequest:
			copyOut := request.(*protocol.CopyOutRequest)

			handle := copyOut.GetHandle()
			srcPath := copyOut.GetSrcPath()
			dstPath := copyOut.GetDstPath()
			owner := copyOut.GetOwner()

			container, err := s.backend.Lookup(handle)
			if err != nil {
				response = errorResponse(err)
				break
			}

			err = container.CopyOut(srcPath, dstPath, owner)
			if err != nil {
				response = errorResponse(err)
				break
			}

			response = &protocol.CopyOutResponse{}
		}

		if response == nil {
			response = &protocol.ErrorResponse{
				Message: proto.String(fmt.Sprintf("unhandled request type: %T", request)),
			}
		}

		protocol.Messages(response).WriteTo(conn)
	}
}

func errorResponse(err error) proto.Message {
	return &protocol.ErrorResponse{
		Message: proto.String(err.Error()),
	}
}
<commit_msg>break request handler up<commit_after>package server

import (
	"fmt"
	"io"
	"log"
	"net"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	"github.com/vito/garden/backend"
	"github.com/vito/garden/messagereader"
	protocol "github.com/vito/garden/protocol"
)

type WardenServer struct {
	socketPath string
	backend    backend.Backend
}

type UnhandledRequestError struct {
	Request proto.Message
}

func (e UnhandledRequestError) Error() string {
	return fmt.Sprintf("unhandled request type: %T", e.Request)
}

func New(socketPath string, backend backend.Backend) *WardenServer {
	return &WardenServer{
		socketPath: socketPath,
		backend:    backend,
	}
}

func (s *WardenServer) Start() error {
	listener, err := net.Listen("unix", s.socketPath)
	if err != nil {
		return err
	}

	go s.handleConnections(listener)

	return nil
}

func (s *WardenServer) handleConnections(listener net.Listener) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println("error accepting connection:", err)
			continue
		}

		go s.serveConnection(conn)
	}
}

func (s *WardenServer) serveConnection(conn net.Conn) {
	for {
		var response proto.Message
		var err error

		request, err := messagereader.ReadRequest(conn)
		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error reading request:", err)
			continue
		}

		switch request.(type) {
		case *protocol.PingRequest:
			response, err = s.handlePing(request.(*protocol.PingRequest))
		case *protocol.EchoRequest:
			response, err = s.handleEcho(request.(*protocol.EchoRequest))
		case *protocol.CreateRequest:
			response, err = s.handleCreate(request.(*protocol.CreateRequest))
		case *protocol.DestroyRequest:
			response, err = s.handleDestroy(request.(*protocol.DestroyRequest))
		case *protocol.ListRequest:
			response, err = s.handleList(request.(*protocol.ListRequest))
		case *protocol.CopyInRequest:
			response, err = s.handleCopyIn(request.(*protocol.CopyInRequest))
		case *protocol.CopyOutRequest:
			response, err = s.handleCopyOut(request.(*protocol.CopyOutRequest))
		default:
			err = UnhandledRequestError{request}
		}

		if err != nil {
			response = &protocol.ErrorResponse{
				Message: proto.String(err.Error()),
			}
		}

		protocol.Messages(response).WriteTo(conn)
	}
}

func (s *WardenServer) handlePing(ping *protocol.PingRequest) (proto.Message, error) {
	return &protocol.PingResponse{}, nil
}

func (s *WardenServer) handleEcho(echo *protocol.EchoRequest) (proto.Message, error) {
	return &protocol.EchoResponse{Message: echo.Message}, nil
}

func (s *WardenServer) handleCreate(create *protocol.CreateRequest) (proto.Message, error) {
	bindMounts := []backend.BindMount{}

	for _, bm := range create.GetBindMounts() {
		bindMount := backend.BindMount{
			SrcPath: bm.GetSrcPath(),
			DstPath: bm.GetDstPath(),
			Mode:    backend.BindMountMode(bm.GetMode()),
		}

		bindMounts = append(bindMounts, bindMount)
	}

	container, err := s.backend.Create(backend.ContainerSpec{
		Handle:     create.GetHandle(),
		GraceTime:  time.Duration(create.GetGraceTime()) * time.Second,
		RootFSPath: create.GetRootfs(),
		Network:    create.GetNetwork(),
		BindMounts: bindMounts,
	})

	if err != nil {
		return nil, err
	}

	return &protocol.CreateResponse{
		Handle: proto.String(container.Handle()),
	}, nil
}

func (s *WardenServer) handleDestroy(destroy *protocol.DestroyRequest) (proto.Message, error) {
	handle := destroy.GetHandle()

	err := s.backend.Destroy(handle)
	if err != nil {
		return nil, err
	}

	return &protocol.DestroyResponse{}, nil
}

func (s *WardenServer) handleList(list *protocol.ListRequest) (proto.Message, error) {
	containers, err := s.backend.Containers()
	if err != nil {
		return nil, err
	}

	handles := []string{}

	for _, container := range containers {
		handles = append(handles, container.Handle())
	}

	return &protocol.ListResponse{Handles: handles}, nil
}

func (s *WardenServer) handleCopyOut(copyOut *protocol.CopyOutRequest) (proto.Message, error) {
	handle := copyOut.GetHandle()
	srcPath := copyOut.GetSrcPath()
	dstPath := copyOut.GetDstPath()
	owner := copyOut.GetOwner()

	container, err := s.backend.Lookup(handle)
	if err != nil {
		return nil, err
	}

	err = container.CopyOut(srcPath, dstPath, owner)
	if err != nil {
		return nil, err
	}

	return &protocol.CopyOutResponse{}, nil
}

func (s *WardenServer) handleCopyIn(copyIn *protocol.CopyInRequest) (proto.Message, error) {
	handle := copyIn.GetHandle()
	srcPath := copyIn.GetSrcPath()
	dstPath := copyIn.GetDstPath()

	container, err := s.backend.Lookup(handle)
	if err != nil {
		return nil, err
	}

	err = container.CopyIn(srcPath, dstPath)
	if err != nil {
		return nil, err
	}

	return &protocol.CopyInResponse{}, nil
}
<|endoftext|>


<commit_before>package server

import (
	"os"
	"sync"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/router"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/transport/grpc"
	"github.com/micro/go-micro/util/log"
)

var (
	// Name of the server
	Name = "go.micro.server"
	// Address to bind to
	Address = ":8083"
	// Network address to bind to
	Network = ":9093"
	// Router address to bind to
	Router = ":9094"
)

type srv struct {
	exit    chan struct{}
	service micro.Service
	router  router.Router
	network transport.Transport
	wg      *sync.WaitGroup
}

func newServer(s micro.Service, r router.Router) *srv {
	// NOTE: this will end up being QUIC transport
	// This is not used right now, but it will be in the future version.
	n := grpc.NewTransport(transport.Addrs(Network))

	return &srv{
		exit:    make(chan struct{}),
		service: s,
		router:  r,
		network: n,
		wg:      &sync.WaitGroup{},
	}
}

func (s *srv) start() error {
	log.Log("[server] starting")

	s.wg.Add(1)
	go s.watch()

	return nil
}

func (s *srv) watch() {
	log.Logf("[server] starting local registry watcher")

	defer s.wg.Done()
	w, err := s.service.Client().Options().Registry.Watch()
	if err != nil {
		log.Logf("[server] failed to create registry watch: %v", err)
		return
	}

	s.wg.Add(1)
	go func() {
		defer s.wg.Done()
		<-s.exit
		log.Logf("[server] stopping local registry watcher")
		w.Stop()
	}()

	// watch for changes to services
	for {
		res, err := w.Next()
		if err != nil {
			// TODO: we should return dedicated STOP error
			log.Logf("[server] error watchiing registry: %s", err)
			return
		}

		switch res.Action {
		case "create":
			if len(res.Service.Nodes) > 0 {
				log.Logf("Action: %s, Service: %v", res.Action, res.Service.Name)
			}
		case "delete":
			log.Logf("Action: %s, Service: %v", res.Action, res.Service.Name)
		}
	}
}

func (s *srv) stop() error {
	log.Log("[server] stopping")

	// notify all goroutines to finish
	close(s.exit)

	// wait for all goroutines to finish
	s.wg.Wait()

	return nil
}

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("network")) > 0 {
		Network = ctx.String("network")
	}
	if len(ctx.String("router")) > 0 {
		Router = ctx.String("router")
	}

	// Initialise service
	service := micro.NewService(
		micro.Name(Name),
		micro.Address(Address),
		micro.RegisterTTL(time.Duration(ctx.GlobalInt("register_ttl"))*time.Second),
		micro.RegisterInterval(time.Duration(ctx.GlobalInt("register_interval"))*time.Second),
	)

	// create new router
	r := router.NewRouter(
		router.Address(Router),
		router.Network(Network),
	)

	// create new server and start it
	s := newServer(service, r)

	if err := s.start(); err != nil {
		log.Logf("error starting server: %v", err)
		os.Exit(1)
	}

	// Run service
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// stop the server
	if err := s.stop(); err != nil {
		log.Logf("error stopping server: %v", err)
		os.Exit(1)
	}
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "server",
		Usage: "Run the micro network server",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the micro server address :8083",
				EnvVar: "MICRO_SERVER_ADDRESS",
			},
			cli.StringFlag{
				Name:   "network",
				Usage:  "Set the micro network address :9093",
				EnvVar: "MICRO_NETWORK_ADDRESS",
			},
			cli.StringFlag{
				Name:   "router",
				Usage:  "Set the micro router address :9094",
				EnvVar: "MICRO_ROUTER_ADDRESS",
			},
		},
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<commit_msg>Replaced Transport with Server in router.<commit_after>package server

import (
	"os"
	"sync"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/router"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/transport/grpc"
	"github.com/micro/go-micro/util/log"
)

var (
	// Name of the server
	Name = "go.micro.server"
	// Address to bind to
	Address = ":8083"
	// Network address to bind to
	Network = ":9093"
	// Router address to bind to
	Router = ":9094"
)

type srv struct {
	exit    chan struct{}
	service micro.Service
	router  router.Router
	network server.Server
	wg      *sync.WaitGroup
}

func newServer(s micro.Service, r router.Router) *srv {
	// NOTE: this will end up being QUIC transport
	t := grpc.NewTransport(transport.Addrs(Network))
	n := server.NewServer(server.Transport(t))

	return &srv{
		exit:    make(chan struct{}),
		service: s,
		router:  r,
		network: n,
		wg:      &sync.WaitGroup{},
	}
}

func (s *srv) start() error {
	log.Log("[server] starting")

	s.wg.Add(1)
	go s.watch()

	return nil
}

func (s *srv) watch() {
	log.Logf("[server] starting local registry watcher")

	defer s.wg.Done()
	w, err := s.service.Client().Options().Registry.Watch()
	if err != nil {
		log.Logf("[server] failed to create registry watch: %v", err)
		return
	}

	s.wg.Add(1)
	go func() {
		defer s.wg.Done()
		<-s.exit
		log.Logf("[server] stopping local registry watcher")
		w.Stop()
	}()

	// watch for changes to services
	for {
		res, err := w.Next()
		if err == registry.ErrWatcherStopped {
			log.Logf("[server] watcher stopped")
			return
		}

		if err != nil {
			log.Logf("[server] error watching registry: %s", err)
			return
		}

		switch res.Action {
		case "create":
			if len(res.Service.Nodes) > 0 {
				log.Logf("Action: %s, Service: %v", res.Action, res.Service.Name)
			}
		case "delete":
			log.Logf("Action: %s, Service: %v", res.Action, res.Service.Name)
		}
	}
}

func (s *srv) stop() error {
	log.Log("[server] stopping")

	// notify all goroutines to finish
	close(s.exit)

	// wait for all goroutines to finish
	s.wg.Wait()

	return nil
}

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("network")) > 0 {
		Network = ctx.String("network")
	}
	if len(ctx.String("router")) > 0 {
		Router = ctx.String("router")
	}

	// Initialise service
	service := micro.NewService(
		micro.Name(Name),
		micro.Address(Address),
		micro.RegisterTTL(time.Duration(ctx.GlobalInt("register_ttl"))*time.Second),
		micro.RegisterInterval(time.Duration(ctx.GlobalInt("register_interval"))*time.Second),
	)

	// create new router
	r := router.NewRouter(
		router.Address(Router),
		router.Network(Network),
	)

	// create new server and start it
	s := newServer(service, r)

	if err := s.start(); err != nil {
		log.Logf("error starting server: %v", err)
		os.Exit(1)
	}

	// Run service
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// stop the server
	if err := s.stop(); err != nil {
		log.Logf("error stopping server: %v", err)
		os.Exit(1)
	}
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "server",
		Usage: "Run the micro network server",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the micro server address :8083",
				EnvVar: "MICRO_SERVER_ADDRESS",
			},
			cli.StringFlag{
				Name:   "network",
				Usage:  "Set the micro network address :9093",
				EnvVar: "MICRO_NETWORK_ADDRESS",
			},
			cli.StringFlag{
				Name:   "router",
				Usage:  "Set the micro router address :9094",
				EnvVar: "MICRO_ROUTER_ADDRESS",
			},
		},
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<|endoftext|>


<commit_before>/* Copyright (c) 2014-2015 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package server

import (
	"net/http"

	"context"
	"crypto/tls"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/jasonish/evebox/appcontext"
	"github.com/jasonish/evebox/core"
	"github.com/jasonish/evebox/log"
	"github.com/jasonish/evebox/resources"
	"github.com/jasonish/evebox/server/api"
	"github.com/jasonish/evebox/server/auth"
	"github.com/jasonish/evebox/server/router"
	"github.com/jasonish/evebox/server/sessions"
	"github.com/spf13/viper"
	"golang.org/x/crypto/acme/autocert"
	"net/http/httputil"
	"net/url"
	"path"
	"strings"
	"time"
)

const DEFAULT_PORT = 5636

const SESSION_HEADER = "x-evebox-session-id"

var sessionStore = sessions.NewSessionStore()

func isPublic(r *http.Request) bool {
	path := r.URL.Path

	prefixes := []string{
		"/auth",
		"/public",
		"/api/1/version",
		"/api/1/login",
		"/api/1/logout",
		"/favicon.ico",

		// Agent's do not require authentication at this time.
		"/api/1/submit",
	}

	for _, prefix := range prefixes {
		if strings.HasPrefix(path, prefix) {
			return true
		}
	}

	return false
}

func Redirector(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/":
			r.URL.Path = "/public/index.html"
		case "/index.html":
			r.URL.Path = "/public/index.html"
		case "/favicon.ico":
			r.URL.Path = "/public/favicon.ico"
		}
		handler.ServeHTTP(w, r)
	})
}

var authenticator auth.Authenticator

func SessionHandler(handler http.Handler) http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		ctx := r.Context()
		session := sessionStore.FindSession(r)
		if session != nil {
			ctx = context.WithValue(ctx, "session", session)
		}

		// If public, pass through.
		if isPublic(r) {
			handler.ServeHTTP(w, r.WithContext(ctx))
			return
		}

		session = authenticator.Authenticate(w, r)
		if session != nil {
			context := context.WithValue(r.Context(),
				"session", session)
			handler.ServeHTTP(w, r.WithContext(context))
		}
	})
}

func sessionReaper(sessionStore *sessions.SessionStore) {
	ticker := time.NewTicker(60 * time.Second)
	go func() {
		for {
			<-ticker.C
			log.Debug("Reaping sessions.")
			sessionStore.Reap()
		}
	}()
	log.Info("Session reaper started")
}

type Server struct {
	router  *router.Router
	proxied bool
	context appcontext.AppContext
}

func NewServer(appContext appcontext.AppContext) *Server {

	sessionReaper(sessionStore)

	sessionStore.Header = SESSION_HEADER

	authRequired := appContext.Config.Authentication.Required
	if authRequired {
		authenticationType := appContext.Config.Authentication.Type
		switch authenticationType {
		case "":
			log.Fatal("Authentication requested but no type set.")
		case "username":
			authenticator = auth.NewUsernameAuthenticator(sessionStore)
		case "usernamepassword":
			if appContext.ConfigDB.InMemory {
				log.Fatal("Username/password authentication not supported with in-memory configuration database.")
			}
			authenticator = auth.NewUsernamePasswordAuthenticator(sessionStore,
				appContext.Userstore)
		default:
			log.Fatalf("Unsupported authentication type: %s",
				authenticationType)
		}
	} else {
		log.Info("Authentication disabled.")
		authenticator = auth.NewAnonymousAuthenticator(sessionStore)
	}

	router := router.NewRouter()

	server := &Server{
		router:  router,
		proxied: appContext.Config.Http.ReverseProxy,
		context: appContext,
	}

	if appContext.Config.Authentication.Github.Enabled {
		githubAuthenticator := auth.NewGithub(
			appContext.Config.Authentication.Github,
			appContext.Userstore)
		githubAuthenticator.SessionStore = sessionStore

		router.Handle("/auth/github", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			githubAuthenticator.Handler(w, r)
		}))

		router.Handle("/auth/github/callback", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			githubAuthenticator.Callback(w, r)
		}))
		log.Info("GitHub Oauth2 authentication configured")
	}

	apiContext := api.NewApiContext(&appContext, sessionStore, authenticator)
	apiContext.InitRoutes(router.Subrouter("/api/1"))

	// Static file server, must be last as it serves as the fallback.
	router.Prefix("/public", StaticHandlerFactory(appContext))

	return server
}

type RequestLogWrapper struct {
}

func (w RequestLogWrapper) Write(b []byte) (n int, err error) {
	log.Info("HTTP: %s", strings.TrimSpace(string(b)))
	return len(b), nil
}

func (s *Server) setupHandlers() http.Handler {
	root := http.Handler(s.router.Router)
	root = SessionHandler(root)
	root = VersionHeaderWrapper(root)
	root = Redirector(root)

	if s.context.Config.Http.RequestLogging {
		log.Debug("Enabling HTTP request logging")
		root = handlers.LoggingHandler(RequestLogWrapper{}, root)
	}

	if s.proxied {
		log.Debug("Apply reverse proxy handler")
		root = handlers.ProxyHeaders(root)
	}

	root = handlers.CompressHandler(root)

	return root
}

func (s *Server) startWithAutoCert(host string, port uint16) error {

	if port != 443 {
		log.Warning("Server not running on port 443; Letsencrypt may not work.")
	}

	dataDirectory := viper.GetString("data-directory")
	if dataDirectory == "" {
		log.Fatal("A data directory must be configured to use Lets Encrypt!")
	}

	cacheDir := path.Join(dataDirectory, "certs")

	certManager := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(s.context.Config.LetsEncryptHostname),
		Cache:      autocert.DirCache(cacheDir),
	}

	server := &http.Server{
		Addr: fmt.Sprintf("%s:%d", host, port),
		TLSConfig: &tls.Config{
			GetCertificate: certManager.GetCertificate,
		},
		Handler: s.setupHandlers(),
	}

	log.Info("Starting server on %s:%d with Letsencrypt support for hostname %s",
		host, port, s.context.Config.LetsEncryptHostname)

	return server.ListenAndServeTLS("", "")
}

func (s *Server) Start(host string, port uint16) error {

	config := s.context.Config

	if config.LetsEncryptHostname != "" {
		return s.startWithAutoCert(host, port)
	}

	root := s.setupHandlers()
	listenAddr := fmt.Sprintf("%s:%d", host, port)

	if !config.Http.TlsEnabled {
		log.Info("Listening on %s", listenAddr)
		return http.ListenAndServe(listenAddr, root)
	} else {
		log.Info("Listening with TLS on %s", listenAddr)
		if config.Http.TlsCertificate == "" {
			log.Fatalf("TLS requested but certificate file not provided.")
		}
		keyFile := config.Http.TlsKey
		if keyFile == "" {
			keyFile = config.Http.TlsCertificate
		}
		return http.ListenAndServeTLS(listenAddr,
			config.Http.TlsCertificate,
			keyFile, root)
	}
}

func StaticHandlerFactory(appContext appcontext.AppContext) http.Handler {
	if appContext.Vars.DevWebAppServerUrl != "" {
		log.Notice("Proxying static files to %v.",
			appContext.Vars.DevWebAppServerUrl)
		devServerProxyUrl, err := url.Parse(appContext.Vars.DevWebAppServerUrl)
		if err != nil {
			log.Fatal(err)
		}

		proxy := httputil.NewSingleHostReverseProxy(devServerProxyUrl)
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			stripped := strings.TrimPrefix(r.URL.Path, "/public")
			log.Debug("Proxying %s -> %s", r.URL.Path, stripped)
			r.URL.Path = stripped
			proxy.ServeHTTP(w, r)
		})
	}
	return resources.FileServer{}
}

func VersionHeaderWrapper(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-EveBox-Git-Revision", core.BuildRev)
		handler.ServeHTTP(w, r)
	})
}
<commit_msg>update to dev proxy server for new ng-cli.<commit_after>/* Copyright (c) 2014-2015 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package server

import (
	"net/http"

	"context"
	"crypto/tls"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/jasonish/evebox/appcontext"
	"github.com/jasonish/evebox/core"
	"github.com/jasonish/evebox/log"
	"github.com/jasonish/evebox/resources"
	"github.com/jasonish/evebox/server/api"
	"github.com/jasonish/evebox/server/auth"
	"github.com/jasonish/evebox/server/router"
	"github.com/jasonish/evebox/server/sessions"
	"github.com/spf13/viper"
	"golang.org/x/crypto/acme/autocert"
	"net/http/httputil"
	"net/url"
	"path"
	"strings"
	"time"
)

const DEFAULT_PORT = 5636

const SESSION_HEADER = "x-evebox-session-id"

var sessionStore = sessions.NewSessionStore()

func isPublic(r *http.Request) bool {
	path := r.URL.Path

	prefixes := []string{
		"/auth",
		"/public",
		"/api/1/version",
		"/api/1/login",
		"/api/1/logout",
		"/favicon.ico",

		// Agent's do not require authentication at this time.
		"/api/1/submit",
	}

	for _, prefix := range prefixes {
		if strings.HasPrefix(path, prefix) {
			return true
		}
	}

	return false
}

func Redirector(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/":
			r.URL.Path = "/public/index.html"
		case "/index.html":
			r.URL.Path = "/public/index.html"
		case "/favicon.ico":
			r.URL.Path = "/public/favicon.ico"
		}
		handler.ServeHTTP(w, r)
	})
}

var authenticator auth.Authenticator

func SessionHandler(handler http.Handler) http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		ctx := r.Context()
		session := sessionStore.FindSession(r)
		if session != nil {
			ctx = context.WithValue(ctx, "session", session)
		}

		// If public, pass through.
		if isPublic(r) {
			handler.ServeHTTP(w, r.WithContext(ctx))
			return
		}

		session = authenticator.Authenticate(w, r)
		if session != nil {
			context := context.WithValue(r.Context(),
				"session", session)
			handler.ServeHTTP(w, r.WithContext(context))
		}
	})
}

func sessionReaper(sessionStore *sessions.SessionStore) {
	ticker := time.NewTicker(60 * time.Second)
	go func() {
		for {
			<-ticker.C
			log.Debug("Reaping sessions.")
			sessionStore.Reap()
		}
	}()
	log.Info("Session reaper started")
}

type Server struct {
	router  *router.Router
	proxied bool
	context appcontext.AppContext
}

func NewServer(appContext appcontext.AppContext) *Server {

	sessionReaper(sessionStore)

	sessionStore.Header = SESSION_HEADER

	authRequired := appContext.Config.Authentication.Required
	if authRequired {
		authenticationType := appContext.Config.Authentication.Type
		switch authenticationType {
		case "":
			log.Fatal("Authentication requested but no type set.")
		case "username":
			authenticator = auth.NewUsernameAuthenticator(sessionStore)
		case "usernamepassword":
			if appContext.ConfigDB.InMemory {
				log.Fatal("Username/password authentication not supported with in-memory configuration database.")
			}
			authenticator = auth.NewUsernamePasswordAuthenticator(sessionStore,
				appContext.Userstore)
		default:
			log.Fatalf("Unsupported authentication type: %s",
				authenticationType)
		}
	} else {
		log.Info("Authentication disabled.")
		authenticator = auth.NewAnonymousAuthenticator(sessionStore)
	}

	router := router.NewRouter()

	server := &Server{
		router:  router,
		proxied: appContext.Config.Http.ReverseProxy,
		context: appContext,
	}

	if appContext.Config.Authentication.Github.Enabled {
		githubAuthenticator := auth.NewGithub(
			appContext.Config.Authentication.Github,
			appContext.Userstore)
		githubAuthenticator.SessionStore = sessionStore

		router.Handle("/auth/github", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			githubAuthenticator.Handler(w, r)
		}))

		router.Handle("/auth/github/callback", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			githubAuthenticator.Callback(w, r)
		}))
		log.Info("GitHub Oauth2 authentication configured")
	}

	apiContext := api.NewApiContext(&appContext, sessionStore, authenticator)
	apiContext.InitRoutes(router.Subrouter("/api/1"))

	// Static file server, must be last as it serves as the fallback.
	router.Prefix("/public", StaticHandlerFactory(appContext))

	return server
}

type RequestLogWrapper struct {
}

func (w RequestLogWrapper) Write(b []byte) (n int, err error) {
	log.Info("HTTP: %s", strings.TrimSpace(string(b)))
	return len(b), nil
}

func (s *Server) setupHandlers() http.Handler {
	root := http.Handler(s.router.Router)
	root = SessionHandler(root)
	root = VersionHeaderWrapper(root)
	root = Redirector(root)

	if s.context.Config.Http.RequestLogging {
		log.Debug("Enabling HTTP request logging")
		root = handlers.LoggingHandler(RequestLogWrapper{}, root)
	}

	if s.proxied {
		log.Debug("Apply reverse proxy handler")
		root = handlers.ProxyHeaders(root)
	}

	root = handlers.CompressHandler(root)

	return root
}

func (s *Server) startWithAutoCert(host string, port uint16) error {

	if port != 443 {
		log.Warning("Server not running on port 443; Letsencrypt may not work.")
	}

	dataDirectory := viper.GetString("data-directory")
	if dataDirectory == "" {
		log.Fatal("A data directory must be configured to use Lets Encrypt!")
	}

	cacheDir := path.Join(dataDirectory, "certs")

	certManager := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(s.context.Config.LetsEncryptHostname),
		Cache:      autocert.DirCache(cacheDir),
	}

	server := &http.Server{
		Addr: fmt.Sprintf("%s:%d", host, port),
		TLSConfig: &tls.Config{
			GetCertificate: certManager.GetCertificate,
		},
		Handler: s.setupHandlers(),
	}

	log.Info("Starting server on %s:%d with Letsencrypt support for hostname %s",
		host, port, s.context.Config.LetsEncryptHostname)

	return server.ListenAndServeTLS("", "")
}

func (s *Server) Start(host string, port uint16) error {

	config := s.context.Config

	if config.LetsEncryptHostname != "" {
		return s.startWithAutoCert(host, port)
	}

	root := s.setupHandlers()
	listenAddr := fmt.Sprintf("%s:%d", host, port)

	if !config.Http.TlsEnabled {
		log.Info("Listening on %s", listenAddr)
		return http.ListenAndServe(listenAddr, root)
	} else {
		log.Info("Listening with TLS on %s", listenAddr)
		if config.Http.TlsCertificate == "" {
			log.Fatalf("TLS requested but certificate file not provided.")
		}
		keyFile := config.Http.TlsKey
		if keyFile == "" {
			keyFile = config.Http.TlsCertificate
		}
		return http.ListenAndServeTLS(listenAddr,
			config.Http.TlsCertificate,
			keyFile, root)
	}
}

func StaticHandlerFactory(appContext appcontext.AppContext) http.Handler {
	if appContext.Vars.DevWebAppServerUrl != "" {
		log.Notice("Proxying static files to %v.",
			appContext.Vars.DevWebAppServerUrl)
		devServerProxyUrl, err := url.Parse(appContext.Vars.DevWebAppServerUrl)
		if err != nil {
			log.Fatal(err)
		}

		proxy := httputil.NewSingleHostReverseProxy(devServerProxyUrl)
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			proxy.ServeHTTP(w, r)
		})
	}
	return resources.FileServer{}
}

func VersionHeaderWrapper(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-EveBox-Git-Revision", core.BuildRev)
		handler.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/codegangsta/martini-contrib/binding"
	"github.com/codegangsta/martini-contrib/cors"
	"github.com/codegangsta/martini-contrib/render"
	"github.com/go-martini/martini"
	"github.com/rolandjudd/thingstodo/controllers"
	"github.com/rolandjudd/thingstodo/db"
	"github.com/rolandjudd/thingstodo/models"
)

func NewServer(databaseName string) *martini.ClassicMartini {
	m := martini.Classic()

	// Setup middleware
	m.Use(db.DB(databaseName))
	m.Use(render.Renderer())
	m.Use(cors.Allow(&cors.Options{
		AllowOrigins:     []string{"http://localhost*"},
		AllowMethods:     []string{"POST", "GET"},
		AllowHeaders:     []string{"Origin"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
	}))

	// Setup routes
	m.Get(`/events`, controllers.GetAllEvents)
	m.Get(`/events/:id`, controllers.GetEvent)
	m.Post(`/events`, binding.Json(models.Event{}), binding.ErrorHandler, controllers.AddEvent)

	// TODO Update, Delete for events
	//m.Put(`/events/:id`, UpdateEvent)
	//m.Delete(`/events/:id`, DeleteEvent)

	// Add the router action

	return m
}
<commit_msg>Added routing for static assets<commit_after>package server

import (
	"github.com/codegangsta/martini-contrib/binding"
	"github.com/codegangsta/martini-contrib/cors"
	"github.com/codegangsta/martini-contrib/render"
	"github.com/go-martini/martini"
	"github.com/rolandjudd/thingstodo/controllers"
	"github.com/rolandjudd/thingstodo/db"
	"github.com/rolandjudd/thingstodo/models"
)

func NewServer(databaseName string) *martini.ClassicMartini {
	m := martini.Classic()

	// Setup middleware
	m.Use(db.DB(databaseName))
	m.Use(render.Renderer())
	m.Use(cors.Allow(&cors.Options{
		AllowOrigins:     []string{"http://localhost*"},
		AllowMethods:     []string{"POST", "GET"},
		AllowHeaders:     []string{"Origin"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
	}))

	// Static Assets
	m.Use(martini.Static("frontend/dist"))

	// Setup routes
	m.Get(`/events`, controllers.GetAllEvents)
	m.Get(`/events/:id`, controllers.GetEvent)
	m.Post(`/events`, binding.Json(models.Event{}), binding.ErrorHandler, controllers.AddEvent)

	// TODO Update, Delete for events
	//m.Put(`/events/:id`, UpdateEvent)
	//m.Delete(`/events/:id`, DeleteEvent)

	// Add the router action

	return m
}
<|endoftext|>


<commit_before>package server

import "github.com/nytlabs/st-core/core"

type Update struct {
	Action string      `json:"action"`
	Type   string      `json:"type"`
	Data   interface{} `json:"data"`
}

type BroadcastId struct {
	Id int `json:"id"`
}

type BroadcastLabel struct {
	BroadcastId
	Label string `json:"label"`
}

type BroadcastPosition struct {
	BroadcastId
	Position Position `json:"position"`
}

// type BLOCK
type BroadcastBlockCreate struct {
	Block BlockLedger `json:"block"`
}

type BroadcastBlockLabel struct {
	Block BroadcastLabel `json:"block"`
}

type BroadcastBlockPosition struct {
	Block BroadcastPosition `json:"block"`
}

type BroadcastBlockDelete struct {
	Block BroadcastId `json:"block"`
}

// type GROUP
type BroadcastGroupCreate struct {
	Group Group `json:"group"`
}

type BroadcastGroupLabel struct {
	Group BroadcastLabel `json:"group"`
}

type BroadcastGroupPosition struct {
	Group BroadcastPosition `json:"group"`
}

type BroadcastGroupDelete struct {
	Group BroadcastId `json:"group"`
}

// type SOURCE
type BroadcastSourceCreate struct {
	Source SourceLedger `json:"source"`
}

type BroadcastSourceLabel struct {
	Source BroadcastLabel `json:"source"`
}

type BroadcastSourcePosition struct {
	Source BroadcastPosition `json:"source"`
}

type BroadcastSourceDelete struct {
	Source BroadcastId `json:"source"`
}

// type LINK
type BroadcastLinkCreate struct {
	Link struct {
		BroadcastId
		Source BroadcastId `json:"source"`
		Block  BroadcastId `json:"block"`
	} `json:"link"`
}

type BroadcastLinkDelete struct {
	Link BroadcastId `json:"link"`
}

// type CONNECTION
type BroadcastConnectionCreate struct {
	Connection ConnectionLedger `json:"connection"`
}

type BroadcastConnectionDelete struct {
	Connection BroadcastId `json:"connection"`
}

// type CHILD
type BroadcastGroupChild struct {
	Group BroadcastId `json:"group"`
	Child BroadcastId `json:"child"`
}

// type ROUTE
type BroadcastRouteModify struct {
	ConnectionNode
	Value *core.InputValue `json:"value"`
}

// type PARAM
type BroadcastSourceModify struct {
	BroadcastId
	Param string `json:"param"`
	Value string `json:"value"`
}
<commit_msg>consistant websocket updates<commit_after>package server

import "github.com/nytlabs/st-core/core"

type Update struct {
	Action string      `json:"action"`
	Type   string      `json:"type"`
	Data   interface{} `json:"data"`
}

type wsId struct {
	Id int `json:"id"`
}

type wsLabel struct {
	wsId
	Label string `json:"label"`
}

type wsPosition struct {
	wsId
	Position Position `json:"position"`
}

// type BLOCK
type wsBlock struct {
	Block interface{} `json:"block"`
}

// type GROUP
type wsGroup struct {
	Group interface{} `json:"group"`
}

// type SOURCE
type wsSource struct {
	Source interface{} `json:"source"`
}

// type LINK
type wsLink struct {
	Link interface{} `json:"link"`
}

// type CONNECTION
type wsConnection struct {
	Connection interface{} `json:"connection"`
}

// type CHILD
type wsGroupChild struct {
	Group wsId `json:"group"`
	Child wsId `json:"child"`
}

// type ROUTE
type wsRouteModify struct {
	ConnectionNode
	Value *core.InputValue `json:"value"`
}

// type PARAM
type wsSourceModify struct {
	wsId
	Param string `json:"param"`
	Value string `json:"value"`
}
<|endoftext|>


<commit_before>package gowhoson

import (
	"bytes"
	"net"
	"strings"
	"sync"
	"testing"

	"github.com/tai-ga/gowhoson/pkg/whoson"
	"github.com/urfave/cli"
)

type TestEnv struct {
	tcpconn   *net.TCPListener
	tcpclient *whoson.Client
	wg        *sync.WaitGroup
}

func (te *TestEnv) close() {
	te.tcpconn.Close()
	te.wg.Wait()
}

var te TestEnv

func startTCPServer(t *testing.T) {
	var err error
	if te.wg == nil {
		te.wg = new(sync.WaitGroup)
	}

	if te.tcpconn == nil {
		addrtcp := net.TCPAddr{
			Port: 9876,
			IP:   net.ParseIP("localhost"),
		}
		te.tcpconn, err = net.ListenTCP("tcp", &addrtcp)
		if err != nil {
			t.Fatalf("Error %v", err)
			return
		}
		te.wg.Add(1)
		go func() {
			defer te.wg.Done()
			whoson.ServeTCP(te.tcpconn)
		}()
	}
	return
}

func testWithServer(t *testing.T, testFuncs ...func(*cli.App)) string {
	AppVersions = NewVersions("", "", "")
	var buf bytes.Buffer
	startTCPServer(t)

	app := makeApp()
	app.Writer = &buf
	app.Metadata = map[string]interface{}{
		"config": &whoson.ClientConfig{
			Mode:   "tcp",
			Server: "localhost:9876",
		},
	}

	for _, f := range testFuncs {
		f(app)
	}
	te.close()
	return buf.String()
}

func TestCmd(t *testing.T) {
	out := testWithServer(
		t,
		func(app *cli.App) {
			app.Run([]string{"gowhoson", "client", "login", "1.1.1.1", "TESTSTRING"})
		},
		func(app *cli.App) {
			app.Run([]string{"gowhoson", "client", "query", "1.1.1.1"})
		},
		func(app *cli.App) {
			app.Run([]string{"gowhoson", "client", "logout", "1.1.1.1"})
		},
	)
	for _, s := range []string{"+LOGIN OK", "+TESTSTRING", "+LOGOUT record deleted"} {
		if !strings.Contains(out, s) {
			t.Fatalf("%q should be contained in output of command: %v", s, out)
		}
	}
}
<commit_msg>fix arguments<commit_after>package gowhoson

import (
	"bytes"
	"net"
	"strings"
	"sync"
	"testing"

	"github.com/tai-ga/gowhoson/pkg/whoson"
	"github.com/urfave/cli"
)

type TestEnv struct {
	tcpconn   *net.TCPListener
	tcpclient *whoson.Client
	wg        *sync.WaitGroup
}

func (te *TestEnv) close() {
	te.tcpconn.Close()
	te.wg.Wait()
}

var te TestEnv

func startTCPServer(t *testing.T) {
	var err error
	if te.wg == nil {
		te.wg = new(sync.WaitGroup)
	}

	if te.tcpconn == nil {
		addrtcp := net.TCPAddr{
			Port: 9876,
			IP:   net.ParseIP("localhost"),
		}
		te.tcpconn, err = net.ListenTCP("tcp", &addrtcp)
		if err != nil {
			t.Fatalf("Error %v", err)
			return
		}
		te.wg.Add(1)
		go func() {
			defer te.wg.Done()
			whoson.ServeTCP(te.tcpconn)
		}()
	}
	return
}

func testWithServer(t *testing.T, testFuncs ...func(*cli.App)) string {
	AppVersions = NewVersions("", "")
	var buf bytes.Buffer
	startTCPServer(t)

	app := makeApp()
	app.Writer = &buf
	app.Metadata = map[string]interface{}{
		"config": &whoson.ClientConfig{
			Mode:   "tcp",
			Server: "localhost:9876",
		},
	}

	for _, f := range testFuncs {
		f(app)
	}
	te.close()
	return buf.String()
}

func TestCmd(t *testing.T) {
	out := testWithServer(
		t,
		func(app *cli.App) {
			app.Run([]string{"gowhoson", "client", "login", "1.1.1.1", "TESTSTRING"})
		},
		func(app *cli.App) {
			app.Run([]string{"gowhoson", "client", "query", "1.1.1.1"})
		},
		func(app *cli.App) {
			app.Run([]string{"gowhoson", "client", "logout", "1.1.1.1"})
		},
	)
	for _, s := range []string{"+LOGIN OK", "+TESTSTRING", "+LOGOUT record deleted"} {
		if !strings.Contains(out, s) {
			t.Fatalf("%q should be contained in output of command: %v", s, out)
		}
	}
}
<|endoftext|>


<commit_before>package provider

import (
	"crypto/sha1"
	"encoding/hex"
	"reflect"
	"strings"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/roblox/terraform-provider-maas/pkg/maas"
)

// MACAddress is used by the maas_instance resource
type MACAddress maas.MACAddress

// Instance models the maas_instance Terraform resource
type Instance struct {
	Architecture           string        `optional:"true" forcenew:"true"`
	BootType               string        `optional:"true" forcenew:"true"`
	CPUCount               int           `optional:"true" forcenew:"true"`
	DisableIPv4            bool          `optional:"true" name:"disable_ipv4"`
	DistroSeries           string        `optional:"true" forcenew:"true"`
	Hostname               string        `optional:"true" forcenew:"true"`
	DeployHostname         string        `optional:"true" forcenew:"true"`
	DeployTags             []string      `optional:"true" forcenew:"true"`
	Tags                   []string      `optional:"true" forcenew:"true"`
	ReleaseErase           bool          `optional:"true" forcenew:"true" default:"false"`
	ReleaseEraseSecure     bool          `optional:"true" forcenew:"true" default:"false"`
	ReleaseEraseQuick      bool          `optional:"true" forcenew:"true" default:"false"`
	IPAddresses            []string      `optional:"true" forcenew:"true"`
	MACAddressSet          []MACAddress  `optional:"true" forcenew:"true"`
	Memory                 int           `optional:"true" forcenew:"true"`
	Netboot                bool          `optional:"true" forcenew:"true"`
	OSystem                string        `optional:"true" forcenew:"true"`
	Owner                  string        `optional:"true" forcenew:"true"`
	PhysicalBlockDeviceSet []BlockDevice `optional:"true" forcenew:"true" name:"physicalblockdevice_set"`
	PowerState             string        `optional:"true"`
	PowerType              string        `optional:"true"`
	PXEMac                 []MACAddress  `optional:"true" type:"Set"`
	ResourceURI            string        `optional:"true" forcenew:"true"`
	Routers                []string      `optional:"true"`
	Status                 int           `optional:"true"`
	Storage                int           `optional:"true"`
	SwapSize               int           `optional:"true"`
	SystemID               string        `optional:"true" forcenew:"true"`
	TagNames               []string      `optional:"true"`
	Zone                   []Zone        `optional:"true" type:"Set"`
	UserData               string        `optional:"true" forcenew:"true" statefunc:"true"`
	HWEKernel              string        `optional:"true" forcenew:"true"`
	InstallKVM             bool          `optional:"true" forcenew:"true" default:"false"`
	Comment                string        `optional:"true"`
	Lock                   bool          `optional:"true" default:"false"`
}

// NewInstance creates a new instance from the value of the Terraform resource
func NewInstance(resource *schema.ResourceData) Instance {
	var instance Instance
	st := reflect.TypeOf(instance)
	sv := reflect.ValueOf(instance)

	for i := 0; i < st.NumField(); i++ {
		// Get the name of the schema field
		key := st.Field(i).Name
		if tag, ok := st.Field(i).Tag.Lookup("name"); ok {
			if tag == "-" {
				continue
			}
			key = tag
		}
		key = strings.ToLower(key)

		// Set the value if one exists
		if schemaVal, ok := resource.GetOk(key); ok {
			field := sv.FieldByName(key)
			reflectVal := reflect.ValueOf(schemaVal)
			field.Set(reflectVal)
		}
	}
	return instance
}

// FromMachine updates the instance to reflect the state of a Machine
func (i Instance) FromMachine(m maas.Machine) Instance {
	return i
}

// GetMetadata implements the Endpoint interface
func (i *Instance) GetMetadata() interface{} {
	return maas.Machine
}

// UpdateState updates the Terraform state to match the Instance state
func (i Instance) UpdateState(resource *schema.ResourceData) {
	st := reflect.TypeOf(i)
	sv := reflect.ValueOf(i)

	for i := 0; i < st.NumField(); i++ {
		// Get the name of the schema field
		key := st.Field(i).Name
		if tag, ok := st.Field(i).Tag.Lookup("name"); ok {
			if tag == "-" {
				continue
			}
			key = tag
		}
		key = strings.ToLower(key)

		// Set the value on the resource if it is not a zero value
		field := sv.FieldByName(key)
		if field.IsValid() {
			resource.Set(key, field.Interface())
		}
	}
}

// AllocateParams creates parameters based on the current value of the Instance
func (i Instance) AllocateParams() maas.MachinesAllocateParams {
	var params maas.MachinesAllocateParams
	if i.SystemID != "" {
		params.SystemID = i.SystemID
		return params
	}
	return params
}

// DeployParams creates parameters based on the current value of the Instance
func (i Instance) DeployParams() maas.MachineDeployParams {
	var params maas.MachineDeployParams
	return params
}

func (i Instance) UserDataStateFunc(v interface{}) string {
	switch v.(type) {
	case string:
		hash := sha1.Sum([]byte(v.(string)))
		return hex.EncodeToString(hash[:])
	default:
		return ""
	}
}
<commit_msg>Fix broken changes from b76241d<commit_after>package provider

import (
	"crypto/sha1"
	"encoding/hex"
	"reflect"
	"strings"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/roblox/terraform-provider-maas/pkg/maas"
)

// MACAddress is used by the maas_instance resource
type MACAddress maas.MACAddress

// Instance models the maas_instance Terraform resource
type Instance struct {
	Architecture           string        `optional:"true" forcenew:"true"`
	BootType               string        `optional:"true" forcenew:"true"`
	CPUCount               int           `optional:"true" forcenew:"true"`
	DisableIPv4            bool          `optional:"true" name:"disable_ipv4"`
	DistroSeries           string        `optional:"true" forcenew:"true"`
	Hostname               string        `optional:"true" forcenew:"true"`
	DeployHostname         string        `optional:"true" forcenew:"true"`
	DeployTags             []string      `optional:"true" forcenew:"true"`
	Tags                   []string      `optional:"true" forcenew:"true"`
	ReleaseErase           bool          `optional:"true" forcenew:"true" default:"false"`
	ReleaseEraseSecure     bool          `optional:"true" forcenew:"true" default:"false"`
	ReleaseEraseQuick      bool          `optional:"true" forcenew:"true" default:"false"`
	IPAddresses            []string      `optional:"true" forcenew:"true"`
	MACAddressSet          []MACAddress  `optional:"true" forcenew:"true"`
	Memory                 int           `optional:"true" forcenew:"true"`
	Netboot                bool          `optional:"true" forcenew:"true"`
	OSystem                string        `optional:"true" forcenew:"true"`
	Owner                  string        `optional:"true" forcenew:"true"`
	PhysicalBlockDeviceSet []BlockDevice `optional:"true" forcenew:"true" name:"physicalblockdevice_set"`
	PowerState             string        `optional:"true"`
	PowerType              string        `optional:"true"`
	PXEMac                 []MACAddress  `optional:"true" type:"Set"`
	ResourceURI            string        `optional:"true" forcenew:"true"`
	Routers                []string      `optional:"true"`
	Status                 int           `optional:"true"`
	Storage                int           `optional:"true"`
	SwapSize               int           `optional:"true"`
	SystemID               string        `optional:"true" forcenew:"true"`
	TagNames               []string      `optional:"true"`
	Zone                   []Zone        `optional:"true" type:"Set"`
	UserData               string        `optional:"true" forcenew:"true" statefunc:"true"`
	HWEKernel              string        `optional:"true" forcenew:"true"`
	InstallKVM             bool          `optional:"true" forcenew:"true" default:"false"`
	Comment                string        `optional:"true"`
	Lock                   bool          `optional:"true" default:"false"`
}

// NewInstance creates a new instance from the value of the Terraform resource
func NewInstance(resource *schema.ResourceData) Instance {
	var instance Instance
	st := reflect.TypeOf(instance)
	sv := reflect.ValueOf(instance)

	for i := 0; i < st.NumField(); i++ {
		// Get the name of the schema field
		key := st.Field(i).Name
		if tag, ok := st.Field(i).Tag.Lookup("name"); ok {
			if tag == "-" {
				continue
			}
			key = tag
		}
		key = strings.ToLower(key)

		// Set the value if one exists
		if schemaVal, ok := resource.GetOk(key); ok {
			field := sv.FieldByName(key)
			reflectVal := reflect.ValueOf(schemaVal)
			field.Set(reflectVal)
		}
	}
	return instance
}

// FromMachine updates the instance to reflect the state of a Machine
func (i Instance) FromMachine(m maas.Machine) Instance {
	return i
}

// GetMetadata implements the Endpoint interface
func (i *Instance) GetMetadata() interface{} {
	return maas.Machine{}
}

// UpdateState updates the Terraform state to match the Instance state
func (i Instance) UpdateState(resource *schema.ResourceData) {
	st := reflect.TypeOf(i)
	sv := reflect.ValueOf(i)

	for i := 0; i < st.NumField(); i++ {
		// Get the name of the schema field
		key := st.Field(i).Name
		if tag, ok := st.Field(i).Tag.Lookup("name"); ok {
			if tag == "-" {
				continue
			}
			key = tag
		}
		key = strings.ToLower(key)

		// Set the value on the resource if it is not a zero value
		field := sv.FieldByName(key)
		if field.IsValid() {
			resource.Set(key, field.Interface())
		}
	}
}

// AllocateParams creates parameters based on the current value of the Instance
func (i Instance) AllocateParams() maas.MachinesAllocateParams {
	var params maas.MachinesAllocateParams
	if i.SystemID != "" {
		params.SystemID = i.SystemID
		return params
	}
	return params
}

// DeployParams creates parameters based on the current value of the Instance
func (i Instance) DeployParams() maas.MachineDeployParams {
	var params maas.MachineDeployParams
	return params
}

func (i Instance) UserDataStateFunc(v interface{}) string {
	switch v.(type) {
	case string:
		hash := sha1.Sum([]byte(v.(string)))
		return hex.EncodeToString(hash[:])
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>package stats

import (
	"fmt"
	"github.com/callpraths/gorobdd/internal/node"
)

func CountNodes(n *node.Node) (int, error) {
	m := make(map[*node.Node]bool)
	return countNodesHelper(n, m)
}

func countNodesHelper(n *node.Node, m map[*node.Node]bool) (int, error) {
	_, seen := m[n]
	if seen {
		return 0, nil
	}
	m[n] = true
	switch n.Type {
	case node.LeafType:
		return 1, nil
	case node.InternalType:
		t, et := countNodesHelper(n.True, m)
		if et != nil {
			return t, et
		}
		f, ef := countNodesHelper(n.False, m)
		if ef != nil {
			return f, ef
		}
		return t + f + 1, nil
	default:
		return -1, fmt.Errorf("Malformed node: %v", n)
	}

}
<commit_msg>Implement CountNodes using the seen tag.<commit_after>package stats

import (
	"fmt"
	"github.com/callpraths/gorobdd/internal/node"
	"github.com/callpraths/gorobdd/internal/tag"
)

func CountNodes(n *node.Node) (int, error) {
	s := tag.NewSeenContext()
	return countNodesHelper(n, s)
}

func countNodesHelper(n *node.Node, s tag.SeenContext) (int, error) {
	if s.IsSeen(n) {
		return 0, nil
	}
	s.MarkSeen(n)
	switch n.Type {
	case node.LeafType:
		return 1, nil
	case node.InternalType:
		t, et := countNodesHelper(n.True, s)
		if et != nil {
			return t, et
		}
		f, ef := countNodesHelper(n.False, s)
		if ef != nil {
			return f, ef
		}
		return t + f + 1, nil
	default:
		return -1, fmt.Errorf("Malformed node: %v", n)
	}

}
<|endoftext|>