proxy: start on api and caddy based proxy
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
parent
15619b08f8
commit
a89aa40dda
24 changed files with 461 additions and 48 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.swp
|
||||||
|
config.toml
|
2
cmd/element/.gitignore
vendored
2
cmd/element/.gitignore
vendored
|
@ -1 +1 @@
|
||||||
coupler
|
element
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
ListenAddr = ":8080"
|
|
|
@ -2,6 +2,6 @@ package config
|
||||||
|
|
||||||
// Config is the top level configuration
|
// Config is the top level configuration
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ListenAddr string
|
ListenAddr string
|
||||||
EnableMetrics bool
|
SocketPath string
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,12 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultListenAddr = ":8080"
|
||||||
|
defaultSocketPath = "/var/run/element.sock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseConfig returns a Config object from a raw string config TOML
|
// ParseConfig returns a Config object from a raw string config TOML
|
||||||
|
@ -11,5 +17,15 @@ func ParseConfig(data string) (*Config, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.ListenAddr == "" {
|
||||||
|
logrus.Warnf("using default listen addr: %s", defaultListenAddr)
|
||||||
|
cfg.ListenAddr = defaultListenAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.SocketPath == "" {
|
||||||
|
logrus.Warnf("using default socket path: %s", defaultSocketPath)
|
||||||
|
cfg.SocketPath = defaultSocketPath
|
||||||
|
}
|
||||||
|
|
||||||
return &cfg, nil
|
return &cfg, nil
|
||||||
}
|
}
|
||||||
|
|
48
proxy/config.go
Normal file
48
proxy/config.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Frontends map[string]*Frontend `json:"frontends,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Frontend struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Hosts []string `json:"hosts,omitempty"`
|
||||||
|
Backend *Backend `json:"backend,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Backend struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Upstreams []string `json:"upstreams,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Body() []byte {
|
||||||
|
t := template.New("proxy")
|
||||||
|
tmpl, err := t.Parse(configTemplate)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error parsing proxy template: %s", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&b, c); err != nil {
|
||||||
|
logrus.Errorf("error executing proxy template: %s", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Path() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) ServerType() string {
|
||||||
|
return "http"
|
||||||
|
}
|
49
proxy/frontend.go
Normal file
49
proxy/frontend.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
func (p *Proxy) AddFrontend(f *Frontend) error {
|
||||||
|
p.m.Lock()
|
||||||
|
defer p.m.Unlock()
|
||||||
|
|
||||||
|
if _, ok := p.config.Frontends[f.Name]; ok {
|
||||||
|
return ErrFrontendExists
|
||||||
|
}
|
||||||
|
|
||||||
|
p.config.Frontends[f.Name] = f
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"name": f.Name,
|
||||||
|
"hosts": f.Hosts,
|
||||||
|
}).Debug("frontend added")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) RemoveFrontend(name string) error {
|
||||||
|
p.m.Lock()
|
||||||
|
defer p.m.Unlock()
|
||||||
|
|
||||||
|
if _, ok := p.config.Frontends[name]; ok {
|
||||||
|
delete(p.config.Frontends, name)
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"name": name,
|
||||||
|
}).Debug("frontend removed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) UpdateFrontend(f *Frontend) error {
|
||||||
|
p.m.Lock()
|
||||||
|
defer p.m.Unlock()
|
||||||
|
|
||||||
|
if _, ok := p.config.Frontends[f.Name]; !ok {
|
||||||
|
return ErrFrontendDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
p.config.Frontends[f.Name] = f
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"name": f.Name,
|
||||||
|
}).Debug("frontend updated")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
38
proxy/proxy.go
Normal file
38
proxy/proxy.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ehazlett/element/version"
|
||||||
|
"github.com/mholt/caddy"
|
||||||
|
_ "github.com/mholt/caddy/caddyhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFrontendExists = errors.New("frontend exists")
|
||||||
|
ErrFrontendDoesNotExist = errors.New("frontend does not exist")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Proxy struct {
|
||||||
|
config *Config
|
||||||
|
instance *caddy.Instance
|
||||||
|
m sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxy(config *Config) (*Proxy, error) {
|
||||||
|
if config.Frontends == nil {
|
||||||
|
config.Frontends = map[string]*Frontend{}
|
||||||
|
}
|
||||||
|
caddy.AppName = "element"
|
||||||
|
caddy.AppVersion = version.Version + version.Build
|
||||||
|
|
||||||
|
return &Proxy{
|
||||||
|
config: config,
|
||||||
|
m: sync.Mutex{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) Config() (*Config, error) {
|
||||||
|
return p.config, nil
|
||||||
|
}
|
15
proxy/reload.go
Normal file
15
proxy/reload.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
func (p *Proxy) Reload() error {
|
||||||
|
p.m.Lock()
|
||||||
|
defer p.m.Unlock()
|
||||||
|
|
||||||
|
i, err := p.instance.Restart(p.config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.instance = i
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
17
proxy/start.go
Normal file
17
proxy/start.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import "github.com/mholt/caddy"
|
||||||
|
|
||||||
|
func (p *Proxy) Start() error {
|
||||||
|
p.m.Lock()
|
||||||
|
defer p.m.Unlock()
|
||||||
|
|
||||||
|
i, err := caddy.Start(p.config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.instance = i
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
9
proxy/stop.go
Normal file
9
proxy/stop.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
func (p *Proxy) Stop() error {
|
||||||
|
if p.instance != nil {
|
||||||
|
return p.instance.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
11
proxy/template.go
Normal file
11
proxy/template.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
const configTemplate = ` # element router configuration
|
||||||
|
{{ range $frontend := .Frontends }}
|
||||||
|
# {{ $frontend.Name }}
|
||||||
|
{{ range $host := $frontend.Hosts }}{{ $host }} {
|
||||||
|
proxy {{ $frontend.Backend.Path }}{{ range $upstream := $frontend.Backend.Upstreams }} {{ $upstream }} {{ end }} {
|
||||||
|
transparent
|
||||||
|
}
|
||||||
|
} {{ end }} {{ end }}
|
||||||
|
`
|
10
proxy/update.go
Normal file
10
proxy/update.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
func (p *Proxy) Update(config *Config) error {
|
||||||
|
p.m.Lock()
|
||||||
|
defer p.m.Unlock()
|
||||||
|
|
||||||
|
p.config = config
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
7
proxy/wait.go
Normal file
7
proxy/wait.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
func (p *Proxy) Wait() {
|
||||||
|
if p.instance != nil {
|
||||||
|
p.instance.Wait()
|
||||||
|
}
|
||||||
|
}
|
24
server/add.go
Normal file
24
server/add.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ehazlett/element/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) addFrontend(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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.proxy.AddFrontend(frontend); err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("error adding frontend: %s", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
30
server/config.go
Normal file
30
server/config.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) getConfig(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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(cfg); err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("error serializing config: %s", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getConfigRaw(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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(cfg.Body())
|
||||||
|
}
|
|
@ -2,11 +2,25 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ehazlett/element/version"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) getRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) genericHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("X-Content-Server", "element "+version.FullVersion())
|
logrus.WithFields(logrus.Fields{
|
||||||
w.WriteHeader(http.StatusOK)
|
"host": r.Host,
|
||||||
|
"uri": r.RequestURI,
|
||||||
|
}).Debug("new domain 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)
|
||||||
|
|
||||||
|
// TODO: issue redirect to host to have client re-send and connect to backend
|
||||||
|
|
||||||
|
w.Header().Set("Location", r.RequestURI)
|
||||||
|
w.WriteHeader(http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
13
server/reload.go
Normal file
13
server/reload.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) reload(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
|
||||||
|
}
|
||||||
|
}
|
19
server/remove.go
Normal file
19
server/remove.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) removeFrontend(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)
|
||||||
|
}
|
16
server/router.go
Normal file
16
server/router.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import "github.com/gorilla/mux"
|
||||||
|
|
||||||
|
func (s *Server) router() *mux.Router {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
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("/reload", s.reload).Methods("POST")
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) router() (*mux.Router, error) {
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.HandleFunc("/", s.getRequestHandler).Methods("GET")
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
|
@ -1,44 +1,90 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/ehazlett/element/config"
|
"github.com/ehazlett/element/config"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/ehazlett/element/proxy"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
|
proxy *proxy.Proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(cfg *config.Config) (*Server, error) {
|
func NewServer(cfg *config.Config) (*Server, error) {
|
||||||
|
p, err := proxy.NewProxy(&proxy.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &Server{
|
return &Server{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
proxy: p,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Run() error {
|
func (s *Server) Run() error {
|
||||||
if s.cfg.EnableMetrics {
|
r := s.router()
|
||||||
// start prometheus listener
|
|
||||||
http.Handle("/metrics", prometheus.Handler())
|
srv := &http.Server{
|
||||||
go func() {
|
Handler: r,
|
||||||
if err := http.ListenAndServe(s.cfg.ListenAddr, nil); err != nil {
|
}
|
||||||
logrus.Error("unable to start metric listener: %s", err)
|
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.proxy.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(c, syscall.SIGHUP)
|
||||||
|
go func() {
|
||||||
|
for range c {
|
||||||
|
if err := s.proxy.Reload(); err != nil {
|
||||||
|
logrus.Errorf("error reloading proxy: %s", err)
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
}
|
}()
|
||||||
|
|
||||||
r, err := s.router()
|
s.proxy.Wait()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Handle("/", r)
|
|
||||||
|
|
||||||
if err := http.ListenAndServe(s.cfg.ListenAddr, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) Stop() error {
|
||||||
|
return s.proxy.Stop()
|
||||||
|
}
|
||||||
|
|
24
server/update.go
Normal file
24
server/update.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ehazlett/element/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) updateFrontend(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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.proxy.UpdateFrontend(frontend); err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("error adding frontend: %s", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
32
vendor.conf
32
vendor.conf
|
@ -1,12 +1,30 @@
|
||||||
github.com/prometheus/client_golang 94ff84a9a6ebb5e6eb9172897c221a64df3443bc
|
github.com/gorilla/mux ac112f7d75a0714af1bd86ab17749b31f7809640
|
||||||
|
github.com/gorilla/context 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
|
||||||
|
github.com/mholt/caddy e7f08bff38988c3049b7fda301c52a681af63cd8
|
||||||
github.com/sirupsen/logrus 181d419aa9e2223811b824e8f0b4af96f9ba9302
|
github.com/sirupsen/logrus 181d419aa9e2223811b824e8f0b4af96f9ba9302
|
||||||
golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
|
golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
|
||||||
golang.org/x/sys 0f826bdd13b500be0f1d4004938ad978fcc6031e
|
golang.org/x/sys 0f826bdd13b500be0f1d4004938ad978fcc6031e
|
||||||
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
github.com/gorilla/websocket a69d9f6de432e2c6b296a947d8a5ee88f68522cf
|
||||||
github.com/golang/protobuf 748d386b5c1ea99658fd69fe9f03991ce86a90c1
|
github.com/nu7hatch/gouuid 179d4d0c4d8d407a32af483c2354df1d2c91e6c3
|
||||||
github.com/prometheus/client_model 6f3806018612930941127f2a7c6c453ba2c527d2
|
golang.org/x/net f5079bd7f6f74e23c4d65efa0f4ce14cbd6a3c0f
|
||||||
github.com/prometheus/common 3e6a7635bac6573d43f49f97b47eb9bda195dba8
|
golang.org/x/text 836efe42bb4aa16aaa17b9c155d8813d336ed720
|
||||||
github.com/prometheus/procfs e645f4e5aaa8506fc71d6edbc5c4ff02c04c46f2
|
github.com/russross/blackfriday 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c
|
||||||
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c
|
github.com/naoina/toml e6f5723bf2a66af014955e0888881314cf294129
|
||||||
|
gopkg.in/yaml.v2 25c4ec802a7d637f88d584ab26798e94ad14c13b
|
||||||
|
github.com/naoina/go-stringutil 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
|
||||||
|
github.com/hashicorp/go-syslog b609c7d9de4658cded34a7336b90886c56f9dbdb
|
||||||
|
github.com/lucas-clemente/quic-go 811315e31a0c190e7a9e86c84102e86c9ed2a072
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 a96e63847dc3c67d17befa69c303767e2f84e54f
|
||||||
|
github.com/codahale/aesnicheck 349fcc471aaccc29cd074e1275f1a494323826cd
|
||||||
|
github.com/xenolf/lego 4dde48a9b9916926a8dd4f69639c8dba40930355
|
||||||
|
github.com/miekg/dns 0f3adef2e2201d72e50309a36fc99d8a9d1a4960
|
||||||
|
gopkg.in/square/go-jose.v1 aa2e30fdd1fe9dd3394119af66451ae790d50e0d
|
||||||
|
github.com/hashicorp/golang-lru 0a025b7e63adc15a622f29b0b2c4c3848243bbf6
|
||||||
|
github.com/lucas-clemente/aes12 25700e67be5c860bcc999137275b9ef8b65932bd
|
||||||
|
github.com/lucas-clemente/fnv128a 393af48d391698c6ae4219566bfbdfef67269997
|
||||||
|
github.com/lucas-clemente/quic-go-certificates d2f86524cced5186554df90d92529757d22c1cb6
|
||||||
|
github.com/dustin/go-humanize 259d2a102b871d17f30e3cd9881a642961a1e486
|
||||||
|
github.com/jimstudt/http-authentication 3eca13d6893afd7ecabe15f4445f5d2872a1b012
|
||||||
|
github.com/flynn/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff
|
||||||
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
||||||
github.com/codegangsta/cli 4b90d79a682b4bf685762c7452db20f2a676ecb2
|
github.com/codegangsta/cli 4b90d79a682b4bf685762c7452db20f2a676ecb2
|
||||||
|
|
Loading…
Reference in a new issue