wip: grpc api
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
parent
1dd748e3f2
commit
2eba8d6511
174 changed files with 22012 additions and 11410 deletions
|
@ -6,7 +6,7 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
func (s *Server) getConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) apiGetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
cfg, err := s.proxy.Config()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("error getting config: %s", err), http.StatusInternalServerError)
|
||||
|
@ -19,7 +19,7 @@ func (s *Server) getConfig(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Server) getConfigRaw(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) apiGetConfigRaw(w http.ResponseWriter, r *http.Request) {
|
||||
cfg, err := s.proxy.Config()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("error getting config: %s", err), http.StatusInternalServerError)
|
||||
|
|
52
server/connect.go
Normal file
52
server/connect.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/ehazlett/element/proxy"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (s *Server) connect(host string) error {
|
||||
service, err := s.store.GetServiceByHost(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if service == nil {
|
||||
return ErrServiceNotFound
|
||||
}
|
||||
|
||||
// create container
|
||||
container, err := s.runtime.Create(service.RuntimeSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"id": container.ID(),
|
||||
"endpoint": container.Endpoint(),
|
||||
}).Debug("container created")
|
||||
|
||||
// configure proxy
|
||||
frontend := &proxy.Frontend{
|
||||
Name: service.ID,
|
||||
Hosts: service.Hosts,
|
||||
Backend: &proxy.Backend{
|
||||
Path: "/",
|
||||
Upstreams: []string{container.Endpoint()},
|
||||
},
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"frontend": frontend,
|
||||
}).Debug("configuring proxy")
|
||||
|
||||
if err := s.proxy.AddFrontend(frontend); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// reload
|
||||
if err := s.proxy.Reload(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -9,10 +9,10 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (s *Server) addFrontend(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) apiAddFrontend(w http.ResponseWriter, r *http.Request) {
|
||||
var frontend *proxy.Frontend
|
||||
if err := json.NewDecoder(r.Body).Decode(&frontend); err != nil {
|
||||
http.Error(w, fmt.Sprintf("invalid fronend: %s", err), http.StatusBadRequest)
|
||||
http.Error(w, fmt.Sprintf("invalid frontend: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -20,22 +20,18 @@ func (s *Server) addFrontend(w http.ResponseWriter, r *http.Request) {
|
|||
http.Error(w, fmt.Sprintf("error adding frontend: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) removeFrontend(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) apiRemoveFrontend(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
if err := s.proxy.RemoveFrontend(name); err != nil {
|
||||
http.Error(w, fmt.Sprintf("error removing frontend: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) updateFrontend(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) apiUpdateFrontend(w http.ResponseWriter, r *http.Request) {
|
||||
var frontend *proxy.Frontend
|
||||
if err := json.NewDecoder(r.Body).Decode(&frontend); err != nil {
|
||||
http.Error(w, fmt.Sprintf("invalid fronend: %s", err), http.StatusBadRequest)
|
||||
|
@ -46,6 +42,4 @@ func (s *Server) updateFrontend(w http.ResponseWriter, r *http.Request) {
|
|||
http.Error(w, fmt.Sprintf("error adding frontend: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -13,14 +13,12 @@ func (s *Server) genericHandler(w http.ResponseWriter, r *http.Request) {
|
|||
"uri": r.RequestURI,
|
||||
}).Debug("request")
|
||||
|
||||
// TODO: check and / or configure backend container
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
// TODO: update proxy config with new backend
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
if err := s.connect(r.Host); err != nil {
|
||||
http.Error(w, fmt.Sprintf("error connecting to backend: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: issue redirect to host to have client re-send and connect to backend
|
||||
|
||||
w.Header().Set("Location", r.RequestURI)
|
||||
w.WriteHeader(http.StatusFound)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
func (s *Server) reload(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) apiReload(w http.ResponseWriter, r *http.Request) {
|
||||
if err := s.proxy.Reload(); err != nil {
|
||||
http.Error(w, fmt.Sprintf("error reloading: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
@ -4,15 +4,15 @@ import "github.com/gorilla/mux"
|
|||
|
||||
func (s *Server) router() *mux.Router {
|
||||
r := mux.NewRouter()
|
||||
// generic
|
||||
r.HandleFunc("/", s.genericHandler)
|
||||
r.HandleFunc("/config", s.getConfig).Methods("GET")
|
||||
r.HandleFunc("/config/raw", s.getConfigRaw).Methods("GET")
|
||||
r.HandleFunc("/frontends", s.addFrontend).Methods("POST")
|
||||
r.HandleFunc("/frontends", s.updateFrontend).Methods("PUT")
|
||||
r.HandleFunc("/frontends/{name}", s.removeFrontend).Methods("DELETE")
|
||||
r.HandleFunc("/services", s.registerService).Methods("POST")
|
||||
r.HandleFunc("/services", s.getServices).Methods("GET")
|
||||
r.HandleFunc("/reload", s.reload).Methods("POST")
|
||||
// proxy
|
||||
r.HandleFunc("/config", s.apiGetConfig).Methods("GET")
|
||||
r.HandleFunc("/config/raw", s.apiGetConfigRaw).Methods("GET")
|
||||
r.HandleFunc("/frontends", s.apiAddFrontend).Methods("POST")
|
||||
r.HandleFunc("/frontends", s.apiUpdateFrontend).Methods("PUT")
|
||||
r.HandleFunc("/frontends/{name}", s.apiRemoveFrontend).Methods("DELETE")
|
||||
r.HandleFunc("/reload", s.apiReload).Methods("POST")
|
||||
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ehazlett/element/config"
|
||||
"github.com/ehazlett/element/runtime"
|
||||
"github.com/ehazlett/element/runtime/docker"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidRuntime = errors.New("invalid runtime specified")
|
||||
)
|
||||
|
||||
// loadRuntime loads a runtime from the specified configuration
|
||||
func loadRuntime(cfg *config.Runtime) (runtime.Runtime, error) {
|
||||
var rt runtime.Runtime
|
||||
|
||||
switch cfg.Name {
|
||||
case "docker":
|
||||
r, err := docker.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rt = r
|
||||
default:
|
||||
return nil, ErrInvalidRuntime
|
||||
}
|
||||
|
||||
return rt, nil
|
||||
}
|
|
@ -1,22 +1,30 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
configurationapi "github.com/ehazlett/element/api/services/configuration"
|
||||
"github.com/ehazlett/element/config"
|
||||
"github.com/ehazlett/element/datastore"
|
||||
"github.com/ehazlett/element/proxy"
|
||||
"github.com/ehazlett/element/runtime"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrServiceNotFound = errors.New("service not found")
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
cfg *config.Config
|
||||
proxy *proxy.Proxy
|
||||
runtime runtime.Runtime
|
||||
store datastore.Datastore
|
||||
}
|
||||
|
||||
func NewServer(cfg *config.Config) (*Server, error) {
|
||||
|
@ -29,6 +37,11 @@ func NewServer(cfg *config.Config) (*Server, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
store, err := loadDatastore(cfg.Datastore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := r.List("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -42,47 +55,32 @@ func NewServer(cfg *config.Config) (*Server, error) {
|
|||
cfg: cfg,
|
||||
proxy: p,
|
||||
runtime: r,
|
||||
store: store,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Run() error {
|
||||
r := s.router()
|
||||
grpcServer := grpc.NewServer()
|
||||
configurationapi.RegisterConfigurationServer(grpcServer, s)
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
go func() {
|
||||
// check for existing socket
|
||||
if _, err := os.Stat(s.cfg.SocketPath); err == nil {
|
||||
os.Remove(s.cfg.SocketPath)
|
||||
}
|
||||
l, err := net.Listen("unix", s.cfg.SocketPath)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to start element server: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
srv.Serve(l)
|
||||
}()
|
||||
|
||||
cfg := &proxy.Config{
|
||||
Frontends: map[string]*proxy.Frontend{
|
||||
"element": &proxy.Frontend{
|
||||
Name: "element",
|
||||
Hosts: []string{s.cfg.ListenAddr},
|
||||
Backend: &proxy.Backend{
|
||||
Path: "/",
|
||||
Upstreams: []string{"unix:" + s.cfg.SocketPath},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := s.proxy.Update(cfg); err != nil {
|
||||
l, err := net.Listen("tcp", s.cfg.GRPCAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//cfg := &proxy.Config{
|
||||
// Frontends: map[string]*proxy.Frontend{
|
||||
// "element": &proxy.Frontend{
|
||||
// Name: "element",
|
||||
// Hosts: []string{s.cfg.ListenAddr},
|
||||
// Backend: &proxy.Backend{
|
||||
// Path: "/",
|
||||
// Upstreams: []string{"unix:" + s.cfg.SocketPath},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
//}
|
||||
|
||||
if err := s.proxy.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -99,7 +97,7 @@ func (s *Server) Run() error {
|
|||
}
|
||||
}()
|
||||
|
||||
s.proxy.Wait()
|
||||
grpcServer.Serve(l)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
package server
|
||||
|
||||
import "net/http"
|
||||
|
||||
func (s *Server) getServices(w http.ResponseWriter, r *http.Request) {
|
||||
import (
|
||||
configurationapi "github.com/ehazlett/element/api/services/configuration"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func (s *Server) CreateService(ctx context.Context, req *configurationapi.CreateServiceRequest) (*configurationapi.CreateServiceResponse, error) {
|
||||
// TODO
|
||||
// save to datastore
|
||||
if err := s.store.SaveService(req.Service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *Server) registerService(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *Server) ListServices(ctx context.Context, req *configurationapi.ListServicesRequest) (*configurationapi.ListServicesResponse, error) {
|
||||
// TODO
|
||||
return nil, nil
|
||||
}
|
||||
|
|
57
server/utils.go
Normal file
57
server/utils.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
"github.com/ehazlett/element/config"
|
||||
"github.com/ehazlett/element/datastore"
|
||||
"github.com/ehazlett/element/datastore/memory"
|
||||
"github.com/ehazlett/element/runtime"
|
||||
"github.com/ehazlett/element/runtime/docker"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidRuntime = errors.New("invalid runtime specified")
|
||||
ErrInvalidDatastore = errors.New("invalid datastore specified")
|
||||
)
|
||||
|
||||
// loadRuntime loads a runtime from the specified configuration
|
||||
func loadRuntime(cfg *config.Runtime) (runtime.Runtime, error) {
|
||||
var rt runtime.Runtime
|
||||
|
||||
switch cfg.Name {
|
||||
case "docker":
|
||||
r, err := docker.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rt = r
|
||||
default:
|
||||
return nil, ErrInvalidRuntime
|
||||
}
|
||||
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
// loadDatastore loads a datastore from the specified configuration
|
||||
func loadDatastore(ds string) (datastore.Datastore, error) {
|
||||
u, err := url.Parse(ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var d datastore.Datastore
|
||||
switch u.Scheme {
|
||||
case "memory":
|
||||
m, err := memory.NewMemory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d = m
|
||||
default:
|
||||
return nil, ErrInvalidDatastore
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue