mirror of
https://github.com/niusmallnan/steve.git
synced 2025-08-15 03:53:02 +00:00
Add dashboard to steve
This commit is contained in:
parent
c069f32bbe
commit
82c7877ba3
@ -146,6 +146,17 @@ func ToMiddleware(auth Authenticator) Middleware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AlwaysAdmin(req *http.Request) (user.Info, bool, error) {
|
||||||
|
return &user.DefaultInfo{
|
||||||
|
Name: "admin",
|
||||||
|
UID: "admin",
|
||||||
|
Groups: []string{
|
||||||
|
"system:masters",
|
||||||
|
"system:authenticated",
|
||||||
|
},
|
||||||
|
}, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func Impersonation(req *http.Request) (user.Info, bool, error) {
|
func Impersonation(req *http.Request) (user.Info, bool, error) {
|
||||||
userName := req.Header.Get(transport.ImpersonateUserHeader)
|
userName := req.Header.Get(transport.ImpersonateUserHeader)
|
||||||
if userName == "" {
|
if userName == "" {
|
||||||
|
90
pkg/dashboard/ui.go
Normal file
90
pkg/dashboard/ui.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/rancher/steve/pkg/responsewriter"
|
||||||
|
"github.com/rancher/steve/pkg/schemaserver/parse"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
insecureClient = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func content(uiSetting func() string) http.Handler {
|
||||||
|
return http.FileServer(http.Dir(uiSetting()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Route(next http.Handler, uiSetting func() string) http.Handler {
|
||||||
|
uiContent := responsewriter.NewMiddlewareChain(responsewriter.Gzip,
|
||||||
|
responsewriter.DenyFrameOptions,
|
||||||
|
responsewriter.CacheMiddleware("json", "js", "css")).Handler(content(uiSetting))
|
||||||
|
|
||||||
|
root := mux.NewRouter()
|
||||||
|
root.Path("/dashboard").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
rw.Header().Add("Location", "/dashboard/")
|
||||||
|
rw.WriteHeader(http.StatusFound)
|
||||||
|
})
|
||||||
|
root.PathPrefix("/dashboard/assets").Handler(uiContent)
|
||||||
|
root.PathPrefix("/dashboard/translations").Handler(uiContent)
|
||||||
|
root.PathPrefix("/dashboard/engines-dist").Handler(uiContent)
|
||||||
|
root.Handle("/dashboard/asset-manifest.json", uiContent)
|
||||||
|
root.Handle("/dashboard/index.html", uiContent)
|
||||||
|
root.PathPrefix("/dashboard/").Handler(wrapUI(next, uiSetting))
|
||||||
|
root.NotFoundHandler = next
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if strings.HasPrefix(req.URL.Path, "/k8s/clusters/local") {
|
||||||
|
req.URL.Path = strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local")
|
||||||
|
if req.URL.Path == "" {
|
||||||
|
req.URL.Path = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.ServeHTTP(rw, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapUI(next http.Handler, uiGetter func() string) http.Handler {
|
||||||
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
if parse.IsBrowser(req, true) {
|
||||||
|
path := uiGetter()
|
||||||
|
if strings.HasPrefix(path, "http") {
|
||||||
|
ui(resp, req, path)
|
||||||
|
} else {
|
||||||
|
http.ServeFile(resp, req, path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next.ServeHTTP(resp, req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ui(resp http.ResponseWriter, req *http.Request, url string) {
|
||||||
|
if err := serveIndex(resp, req, url); err != nil {
|
||||||
|
logrus.Errorf("failed to serve UI: %v", err)
|
||||||
|
resp.WriteHeader(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveIndex(resp http.ResponseWriter, req *http.Request, url string) error {
|
||||||
|
r, err := insecureClient.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Body.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(resp, r.Body)
|
||||||
|
return err
|
||||||
|
}
|
49
pkg/responsewriter/cache.go
Normal file
49
pkg/responsewriter/cache.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package responsewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CacheMiddleware(suffixes ...string) mux.MiddlewareFunc {
|
||||||
|
return mux.MiddlewareFunc(func(handler http.Handler) http.Handler {
|
||||||
|
return Cache(handler, suffixes...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Cache(handler http.Handler, suffixes ...string) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
i := strings.LastIndex(r.URL.Path, ".")
|
||||||
|
if i >= 0 {
|
||||||
|
for _, suffix := range suffixes {
|
||||||
|
if suffix == r.URL.Path[i+1:] {
|
||||||
|
w.Header().Set("Cache-Control", "max-age=31536000, public")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NoCache(handler http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func DenyFrameOptions(handler http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("X-Frame-Options", "deny")
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContentTypeOptions(handler http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
42
pkg/responsewriter/content.go
Normal file
42
pkg/responsewriter/content.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package responsewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContentTypeWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ContentTypeWriter) Write(b []byte) (int, error) {
|
||||||
|
found := false
|
||||||
|
for k := range c.Header() {
|
||||||
|
if strings.EqualFold(k, "Content-Type") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
c.Header().Set("Content-Type", http.DetectContentType(b))
|
||||||
|
}
|
||||||
|
return c.ResponseWriter.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContentType(handler http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
writer := ContentTypeWriter{ResponseWriter: w}
|
||||||
|
handler.ServeHTTP(writer, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ContentTypeWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
if hijacker, ok := c.ResponseWriter.(http.Hijacker); ok {
|
||||||
|
return hijacker.Hijack()
|
||||||
|
}
|
||||||
|
return nil, nil, fmt.Errorf("Upstream ResponseWriter of type %v does not implement http.Hijacker", reflect.TypeOf(c.ResponseWriter))
|
||||||
|
}
|
70
pkg/responsewriter/gzip.go
Normal file
70
pkg/responsewriter/gzip.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package responsewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrapWriter struct {
|
||||||
|
gzipResponseWriter
|
||||||
|
|
||||||
|
code int
|
||||||
|
}
|
||||||
|
|
||||||
|
type gzipResponseWriter struct {
|
||||||
|
io.Writer
|
||||||
|
http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g gzipResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
// Header logic is kept here in case the user does not use WriteHeader
|
||||||
|
g.Header().Set("Content-Encoding", "gzip")
|
||||||
|
g.Header().Del("Content-Length")
|
||||||
|
|
||||||
|
return g.Writer.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close uses gzip to write gzip footer if message is gzip encoded
|
||||||
|
func (g gzipResponseWriter) Close(writer *gzip.Writer) {
|
||||||
|
if g.Header().Get("Content-Encoding") == "gzip" {
|
||||||
|
writer.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader sets gzip encoding and removes length. Should always be used when using gzip writer.
|
||||||
|
func (g gzipResponseWriter) WriteHeader(statusCode int) {
|
||||||
|
g.Header().Set("Content-Encoding", "gzip")
|
||||||
|
g.Header().Del("Content-Length")
|
||||||
|
g.ResponseWriter.WriteHeader(statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gzip creates a gzip writer if gzip encoding is accepted.
|
||||||
|
func Gzip(handler http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gz := gzip.NewWriter(w)
|
||||||
|
|
||||||
|
gzw := &wrapWriter{gzipResponseWriter{Writer: gz, ResponseWriter: w}, http.StatusOK}
|
||||||
|
defer gzw.Close(gz)
|
||||||
|
|
||||||
|
// Content encoding will be set once Write or WriteHeader is called, to avoid gzipping empty messages
|
||||||
|
handler.ServeHTTP(gzw, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hijack must be implemented to properly chain with handlers expecting a hijacker handler to be passed
|
||||||
|
func (g *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
if hijacker, ok := g.ResponseWriter.(http.Hijacker); ok {
|
||||||
|
return hijacker.Hijack()
|
||||||
|
}
|
||||||
|
return nil, nil, fmt.Errorf("Upstream ResponseWriter of type %v does not implement http.Hijacker", reflect.TypeOf(g.ResponseWriter))
|
||||||
|
}
|
174
pkg/responsewriter/gzip_test.go
Normal file
174
pkg/responsewriter/gzip_test.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package responsewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// All other writers will attempt additional unnecessary logic
|
||||||
|
// Implements http.responseWriter and io.Writer
|
||||||
|
type DummyWriter struct {
|
||||||
|
header map[string][]string
|
||||||
|
buffer []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type DummyHandler struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type DummyHandlerWithWrite struct {
|
||||||
|
DummyHandler
|
||||||
|
next http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDummyWriter() *DummyWriter {
|
||||||
|
return &DummyWriter{map[string][]string{}, []byte{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRequest(accept string) *http.Request {
|
||||||
|
return &http.Request{
|
||||||
|
Header: map[string][]string{"Accept-Encoding": {accept}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DummyWriter) Header() http.Header {
|
||||||
|
return d.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DummyWriter) Write(p []byte) (n int, err error) {
|
||||||
|
d.buffer = append(d.buffer, p...)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DummyWriter) WriteHeader(int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DummyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DummyHandlerWithWrite) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte{0, 0})
|
||||||
|
if d.next != nil {
|
||||||
|
d.next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestWriteHeader asserts content-length header is deleted and content-encoding header is set to gzip
|
||||||
|
func TestWriteHeader(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
w := NewDummyWriter()
|
||||||
|
gz := &gzipResponseWriter{gzip.NewWriter(w), w}
|
||||||
|
|
||||||
|
gz.Header().Set("Content-Length", "80")
|
||||||
|
gz.WriteHeader(400)
|
||||||
|
// Content-Length should have been deleted in WriterHeader, resulting in empty string
|
||||||
|
assert.Equal("", gz.Header().Get("Content-Length"))
|
||||||
|
assert.Equal(1, len(w.header["Content-Encoding"]))
|
||||||
|
assert.Equal("gzip", gz.Header().Get("Content-Encoding"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSetContentWithoutWrite asserts content-encoding is NOT "gzip" if accept-encoding header does not contain gzip
|
||||||
|
func TestSetContentWithoutWrite(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// Test content encoding header when write is not used
|
||||||
|
handlerFunc := Gzip(&DummyHandler{})
|
||||||
|
|
||||||
|
// Test when accept-encoding only contains gzip
|
||||||
|
rw := NewDummyWriter()
|
||||||
|
req := NewRequest("gzip")
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
// Content encoding should be empty since write has not been used
|
||||||
|
assert.Equal(0, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("", rw.Header().Get("Content-Encoding"))
|
||||||
|
|
||||||
|
// Test when accept-encoding contains multiple options, including gzip
|
||||||
|
rw = NewDummyWriter()
|
||||||
|
req = NewRequest("json, xml, gzip")
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
assert.Equal(0, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("", rw.Header().Get("Content-Encoding"))
|
||||||
|
|
||||||
|
// Test when accept-encoding is empty
|
||||||
|
req = NewRequest("")
|
||||||
|
rw = NewDummyWriter()
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
assert.Equal(0, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("", rw.Header().Get("Content-Encoding"))
|
||||||
|
|
||||||
|
// Test when accept-encoding is is not empty but does not include gzip
|
||||||
|
req = NewRequest("json, xml")
|
||||||
|
rw = NewDummyWriter()
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
assert.Equal(0, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("", rw.Header().Get("Content-Encoding"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSetContentWithWrite asserts content-encoding is "gzip" if accept-encoding header contains gzip
|
||||||
|
func TestSetContentWithWrite(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// Test content encoding header when write is used
|
||||||
|
handlerFunc := Gzip(&DummyHandlerWithWrite{})
|
||||||
|
|
||||||
|
// Test when accept-encoding only contains gzip
|
||||||
|
req := NewRequest("gzip")
|
||||||
|
rw := NewDummyWriter()
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
// Content encoding should be gzip since write has been used
|
||||||
|
assert.Equal(1, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("gzip", rw.Header().Get("Content-Encoding"))
|
||||||
|
|
||||||
|
// Test when accept-encoding contains multiple options, including gzip
|
||||||
|
req = NewRequest("json, xml, gzip")
|
||||||
|
rw = NewDummyWriter()
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
// Content encoding should be gzip since write has been used
|
||||||
|
assert.Equal(1, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("gzip", rw.Header().Get("Content-Encoding"))
|
||||||
|
|
||||||
|
// Test when accept-encoding is empty
|
||||||
|
req = NewRequest("")
|
||||||
|
rw = NewDummyWriter()
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
// Content encoding should be empty since gzip is not an accepted encoding
|
||||||
|
assert.Equal(0, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("", rw.Header().Get("Content-Encoding"))
|
||||||
|
|
||||||
|
// Test when accept-encoding is is not empty but does not include gzip
|
||||||
|
req = NewRequest("json, xml")
|
||||||
|
rw = NewDummyWriter()
|
||||||
|
handlerFunc.ServeHTTP(rw, req)
|
||||||
|
// Content encoding should be empty since gzip is not an accepted encoding
|
||||||
|
assert.Equal(0, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("", rw.Header().Get("Content-Encoding"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMultipleWrites ensures that Write can be used multiple times
|
||||||
|
func TestMultipleWrites(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// Handler function that contains one writing handler
|
||||||
|
handlerFuncOneWrite := Gzip(&DummyHandlerWithWrite{})
|
||||||
|
|
||||||
|
// Handler function that contains a chain of two writing handlers
|
||||||
|
handlerFuncTwoWrites := Gzip(&DummyHandlerWithWrite{next: &DummyHandlerWithWrite{}})
|
||||||
|
|
||||||
|
req := NewRequest("gzip")
|
||||||
|
rw := NewDummyWriter()
|
||||||
|
handlerFuncOneWrite.ServeHTTP(rw, req)
|
||||||
|
oneWriteResult := rw.buffer
|
||||||
|
|
||||||
|
req = NewRequest("gzip")
|
||||||
|
rw = NewDummyWriter()
|
||||||
|
handlerFuncTwoWrites.ServeHTTP(rw, req)
|
||||||
|
multiWriteResult := rw.buffer
|
||||||
|
|
||||||
|
// Content encoding should be gzip since write has been used (twice)
|
||||||
|
assert.Equal(1, len(rw.header["Content-Encoding"]))
|
||||||
|
assert.Equal("gzip", rw.Header().Get("Content-Encoding"))
|
||||||
|
assert.NotEqual(multiWriteResult, oneWriteResult)
|
||||||
|
}
|
24
pkg/responsewriter/middleware.go
Normal file
24
pkg/responsewriter/middleware.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package responsewriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MiddlewareChain struct {
|
||||||
|
middleWares []mux.MiddlewareFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMiddlewareChain(middleWares ...mux.MiddlewareFunc) *MiddlewareChain {
|
||||||
|
return &MiddlewareChain{middleWares: middleWares}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MiddlewareChain) Handler(handler http.Handler) http.Handler {
|
||||||
|
rtn := handler
|
||||||
|
for i := len(m.middleWares) - 1; i >= 0; i-- {
|
||||||
|
w := m.middleWares[i]
|
||||||
|
rtn = w.Middleware(rtn)
|
||||||
|
}
|
||||||
|
return rtn
|
||||||
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
authcli "github.com/rancher/steve/pkg/auth/cli"
|
authcli "github.com/rancher/steve/pkg/auth/cli"
|
||||||
"github.com/rancher/steve/pkg/server"
|
"github.com/rancher/steve/pkg/server"
|
||||||
"github.com/rancher/wrangler/pkg/kubeconfig"
|
"github.com/rancher/wrangler/pkg/kubeconfig"
|
||||||
|
"github.com/rancher/wrangler/pkg/ratelimit"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ type Config struct {
|
|||||||
KubeConfig string
|
KubeConfig string
|
||||||
HTTPSListenPort int
|
HTTPSListenPort int
|
||||||
HTTPListenPort int
|
HTTPListenPort int
|
||||||
|
DashboardURL string
|
||||||
|
|
||||||
WebhookConfig authcli.WebhookConfig
|
WebhookConfig authcli.WebhookConfig
|
||||||
}
|
}
|
||||||
@ -28,6 +30,7 @@ func (c *Config) ToServer() (*server.Server, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
restConfig.RateLimiter = ratelimit.None
|
||||||
|
|
||||||
auth, err := c.WebhookConfig.WebhookMiddleware()
|
auth, err := c.WebhookConfig.WebhookMiddleware()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -37,6 +40,9 @@ func (c *Config) ToServer() (*server.Server, error) {
|
|||||||
return &server.Server{
|
return &server.Server{
|
||||||
RestConfig: restConfig,
|
RestConfig: restConfig,
|
||||||
AuthMiddleware: auth,
|
AuthMiddleware: auth,
|
||||||
|
DashboardURL: func() string {
|
||||||
|
return c.DashboardURL
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +63,11 @@ func Flags(config *Config) []cli.Flag {
|
|||||||
Value: 8080,
|
Value: 8080,
|
||||||
Destination: &config.HTTPListenPort,
|
Destination: &config.HTTPListenPort,
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "dashboard-url",
|
||||||
|
Value: "https://releases.rancher.com/dashboard/latest/index.html",
|
||||||
|
Destination: &config.DashboardURL,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(flags, authcli.Flags(&config.WebhookConfig)...)
|
return append(flags, authcli.Flags(&config.WebhookConfig)...)
|
||||||
|
@ -37,6 +37,7 @@ type Server struct {
|
|||||||
Router router.RouterFunc
|
Router router.RouterFunc
|
||||||
PostStartHooks []func() error
|
PostStartHooks []func() error
|
||||||
StartHooks []StartHook
|
StartHooks []StartHook
|
||||||
|
DashboardURL func() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Controllers struct {
|
type Controllers struct {
|
||||||
|
@ -33,6 +33,7 @@ func New(cfg *rest.Config, sf schema.Factory, authMiddleware auth.Middleware, ne
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
authMiddleware = auth.ToMiddleware(auth.AuthenticatorFunc(auth.AlwaysAdmin))
|
||||||
} else {
|
} else {
|
||||||
proxy = k8sproxy.ImpersonatingHandler("/", cfg)
|
proxy = k8sproxy.ImpersonatingHandler("/", cfg)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/rancher/steve/pkg/client"
|
"github.com/rancher/steve/pkg/client"
|
||||||
"github.com/rancher/steve/pkg/clustercache"
|
"github.com/rancher/steve/pkg/clustercache"
|
||||||
schemacontroller "github.com/rancher/steve/pkg/controllers/schema"
|
schemacontroller "github.com/rancher/steve/pkg/controllers/schema"
|
||||||
|
"github.com/rancher/steve/pkg/dashboard"
|
||||||
"github.com/rancher/steve/pkg/schema"
|
"github.com/rancher/steve/pkg/schema"
|
||||||
"github.com/rancher/steve/pkg/schemaserver/types"
|
"github.com/rancher/steve/pkg/schemaserver/types"
|
||||||
"github.com/rancher/steve/pkg/server/handler"
|
"github.com/rancher/steve/pkg/server/handler"
|
||||||
@ -87,6 +88,10 @@ func setup(ctx context.Context, server *Server) (http.Handler, *schema.Collectio
|
|||||||
return sync()
|
return sync()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if server.DashboardURL != nil && server.DashboardURL() != "" {
|
||||||
|
handler = dashboard.Route(handler, server.DashboardURL)
|
||||||
|
}
|
||||||
|
|
||||||
return handler, sf, nil
|
return handler, sf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user