refactor: UI resource logic

- Support embed api-ui resources
- The ui-path arg will be applied if provided. Also applied to api-ui resource files
This commit is contained in:
Yuxing Deng 2024-07-23 16:24:03 +08:00
parent 004e4751c8
commit eacc47482e
18 changed files with 452 additions and 228 deletions

View File

@ -20,6 +20,7 @@ RUN if [ "${ARCH}" == "amd64" ]; then \
fi fi
COPY --from=tools /app/release-notary /usr/local/bin/ COPY --from=tools /app/release-notary /usr/local/bin/
ENV CATTLE_DASHBOARD_UI_VERSION="v2.8.0-kube-explorer-ui-rc3" ENV CATTLE_DASHBOARD_UI_VERSION="v2.8.0-kube-explorer-ui-rc3"
ENV CATTLE_API_UI_VERSION="1.1.11"
ENV DAPPER_ENV REPO TAG DRONE_TAG CROSS GOPROXY SKIP_COMPRESS GITHUB_REPOSITORY GITHUB_TOKEN ENV DAPPER_ENV REPO TAG DRONE_TAG CROSS GOPROXY SKIP_COMPRESS GITHUB_REPOSITORY GITHUB_TOKEN
ENV DAPPER_SOURCE /go/src/github.com/cnrancher/kube-explorer ENV DAPPER_SOURCE /go/src/github.com/cnrancher/kube-explorer

View File

@ -6,6 +6,7 @@ import (
var InsecureSkipTLSVerify bool var InsecureSkipTLSVerify bool
var SystemDefaultRegistry string var SystemDefaultRegistry string
var APIUIVersion = "1.1.11"
var ShellPodImage string var ShellPodImage string
@ -24,5 +25,11 @@ func Flags() []cli.Flag {
Destination: &ShellPodImage, Destination: &ShellPodImage,
Value: "rancher/shell:v0.2.1-rc.7", Value: "rancher/shell:v0.2.1-rc.7",
}, },
cli.StringFlag{
Name: "apiui-version",
Hidden: true,
Destination: &APIUIVersion,
Value: APIUIVersion,
},
} }
} }

11
internal/config/steve.go Normal file
View File

@ -0,0 +1,11 @@
package config
import (
"github.com/rancher/steve/pkg/debug"
stevecli "github.com/rancher/steve/pkg/server/cli"
)
var (
Steve stevecli.Config
Debug debug.Config
)

View File

@ -17,6 +17,7 @@ import (
"github.com/cnrancher/kube-explorer/internal/config" "github.com/cnrancher/kube-explorer/internal/config"
"github.com/cnrancher/kube-explorer/internal/resources/cluster" "github.com/cnrancher/kube-explorer/internal/resources/cluster"
"github.com/cnrancher/kube-explorer/internal/ui" "github.com/cnrancher/kube-explorer/internal/ui"
"github.com/cnrancher/kube-explorer/internal/version"
) )
func ToServer(ctx context.Context, c *cli.Config, sqlCache bool) (*server.Server, error) { func ToServer(ctx context.Context, c *cli.Config, sqlCache bool) (*server.Server, error) {
@ -48,10 +49,15 @@ func ToServer(ctx context.Context, c *cli.Config, sqlCache bool) (*server.Server
return nil, err return nil, err
} }
ui, apiui := ui.New(&ui.Options{
ReleaseSetting: version.IsRelease,
Path: func() string { return c.UIPath },
})
steveServer, err := server.New(ctx, restConfig, &server.Options{ steveServer, err := server.New(ctx, restConfig, &server.Options{
AuthMiddleware: auth, AuthMiddleware: auth,
Controllers: controllers, Controllers: controllers,
Next: ui.New(c.UIPath), Next: ui,
SQLCache: sqlCache, SQLCache: sqlCache,
// router needs to hack here // router needs to hack here
Router: func(h router.Handlers) http.Handler { Router: func(h router.Handlers) http.Handler {
@ -62,6 +68,8 @@ func ToServer(ctx context.Context, c *cli.Config, sqlCache bool) (*server.Server
return nil, err return nil, err
} }
steveServer.APIServer.CustomAPIUIResponseWriter(apiui.CSS(), apiui.JS(), func() string { return config.APIUIVersion })
// registrer local cluster // registrer local cluster
if err := cluster.Register(ctx, steveServer, c.Context); err != nil { if err := cluster.Register(ctx, steveServer, c.Context); err != nil {
return steveServer, err return steveServer, err

55
internal/ui/apiui.go Normal file
View File

@ -0,0 +1,55 @@
package ui
import "github.com/rancher/apiserver/pkg/writer"
type APIUI struct {
offline StringSetting
release BoolSetting
embed bool
}
func apiUI(opt *Options) APIUI {
var rtn = APIUI{
offline: opt.Offline,
release: opt.ReleaseSetting,
embed: true,
}
if rtn.offline == nil {
rtn.offline = StaticSetting("dynamic")
}
if rtn.release == nil {
rtn.release = StaticSetting(false)
}
for _, file := range []string{
"ui/api-ui/ui.min.css",
"ui/api-ui/ui.min.js",
} {
if _, err := staticContent.Open(file); err != nil {
rtn.embed = false
break
}
}
return rtn
}
func (a APIUI) content(name string) writer.StringGetter {
return func() (rtn string) {
switch a.offline() {
case "dynamic":
if !a.release() && !a.embed {
return ""
}
case "false":
return ""
}
return name
}
}
func (a APIUI) CSS() writer.StringGetter {
return a.content("/api-ui/ui.min.css")
}
func (a APIUI) JS() writer.StringGetter {
return a.content("/api-ui/ui.min.js")
}

View File

@ -0,0 +1,24 @@
package content
import (
"io/fs"
"net/http"
)
type fsFunc func(name string) (fs.File, error)
func (f fsFunc) Open(name string) (fs.File, error) {
return f(name)
}
type fsContent interface {
ToFileServer(basePaths ...string) http.Handler
Open(name string) (fs.File, error)
}
type Handler interface {
ServeAssets(middleware func(http.Handler) http.Handler, hext http.Handler) http.Handler
ServeFaviconDashboard() http.Handler
GetIndex() ([]byte, error)
Refresh()
}

View File

@ -0,0 +1,97 @@
package content
import (
"bytes"
"crypto/tls"
"errors"
"io"
"net/http"
"sync"
)
const (
defaultIndex = "https://releases.rancher.com/dashboard/latest/index.html"
)
func NewExternal(getIndex func() string) Handler {
return &externalIndexHandler{
getIndexFunc: getIndex,
}
}
var (
insecureClient = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
_ Handler = &externalIndexHandler{}
)
type externalIndexHandler struct {
sync.RWMutex
getIndexFunc func() string
current string
downloadSuccess *bool
}
func (u *externalIndexHandler) ServeAssets(_ func(http.Handler) http.Handler, next http.Handler) http.Handler {
return next
}
func (u *externalIndexHandler) ServeFaviconDashboard() http.Handler {
return http.NotFoundHandler()
}
func (u *externalIndexHandler) GetIndex() ([]byte, error) {
if u.canDownload() {
var buffer bytes.Buffer
if err := serveIndex(&buffer, u.current); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
return nil, errors.New("external index is not available")
}
func serveIndex(resp io.Writer, 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
}
func (u *externalIndexHandler) canDownload() bool {
u.RLock()
rtn := u.downloadSuccess
u.RUnlock()
if rtn != nil {
return *rtn
}
return u.refresh()
}
func (u *externalIndexHandler) refresh() bool {
u.Lock()
defer u.RUnlock()
u.current = u.getIndexFunc()
if u.current == "" {
u.current = defaultIndex
}
t := serveIndex(io.Discard, u.current) == nil
u.downloadSuccess = &t
return t
}
func (u *externalIndexHandler) Refresh() {
_ = u.refresh()
}

71
internal/ui/content/fs.go Normal file
View File

@ -0,0 +1,71 @@
package content
import (
"io"
"net/http"
"path/filepath"
"sync"
)
var _ Handler = &handler{}
func newFS(content fsContent) Handler {
return &handler{
content: content,
cacheFS: &sync.Map{},
}
}
type handler struct {
content fsContent
cacheFS *sync.Map
}
func (h *handler) pathExist(path string) bool {
_, err := h.content.Open(path)
return err == nil
}
func (h *handler) serveContent(basePaths ...string) http.Handler {
key := filepath.Join(basePaths...)
if rtn, ok := h.cacheFS.Load(key); ok {
return rtn.(http.Handler)
}
rtn := h.content.ToFileServer(basePaths...)
h.cacheFS.Store(key, rtn)
return rtn
}
func (h *handler) Refresh() {
h.cacheFS.Range(func(key, _ any) bool {
h.cacheFS.Delete(key)
return true
})
}
func (h *handler) ServeAssets(middleware func(http.Handler) http.Handler, next http.Handler) http.Handler {
assets := middleware(h.serveContent())
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if h.pathExist(r.URL.Path) {
assets.ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
func (h *handler) ServeFaviconDashboard() http.Handler {
return h.serveContent("dashboard")
}
func (h *handler) GetIndex() ([]byte, error) {
path := filepath.Join("dashboard", "index.html")
f, err := h.content.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
return io.ReadAll(f)
}

View File

@ -0,0 +1,43 @@
package content
import (
"embed"
"io/fs"
"net/http"
"path/filepath"
)
func NewEmbedded(staticContent embed.FS, prefix string) Handler {
return newFS(&embedFS{
pathPrefix: prefix,
staticContent: staticContent,
})
}
var _ fsContent = &embedFS{}
type embedFS struct {
pathPrefix string
staticContent embed.FS
}
// Open implements fsContent.
func (e *embedFS) Open(name string) (fs.File, error) {
return e.staticContent.Open(joinEmbedFilepath(e.pathPrefix, name))
}
// ToFileServer implements fsContent.
func (e *embedFS) ToFileServer(basePaths ...string) http.Handler {
handler := fsFunc(func(name string) (fs.File, error) {
assetPath := joinEmbedFilepath(joinEmbedFilepath(basePaths...), name)
return e.Open(assetPath)
})
return http.FileServer(http.FS(handler))
}
func (e *embedFS) Refresh() error { return nil }
func joinEmbedFilepath(paths ...string) string {
return filepath.ToSlash(filepath.Join(paths...))
}

View File

@ -0,0 +1,41 @@
package content
import (
"errors"
"io/fs"
"net/http"
"path/filepath"
)
func NewFilepath(getPath func() string) Handler {
return newFS(&filepathFS{
getPath: getPath,
})
}
var _ fsContent = &filepathFS{}
type filepathFS struct {
getPath func() string
}
func (f *filepathFS) ToFileServer(basePaths ...string) http.Handler {
root := f.getPath()
if root == "" {
return http.NotFoundHandler()
}
path := filepath.Join(append([]string{string(root)}, basePaths...)...)
return http.FileServer(http.Dir(path))
}
func (f *filepathFS) Open(name string) (fs.File, error) {
root := f.getPath()
if root == "" {
return nil, errors.New("filepath fs is not ready")
}
return http.Dir(root).Open(name)
}
func (f *filepathFS) Refresh() error {
return nil
}

7
internal/ui/dev.go Normal file
View File

@ -0,0 +1,7 @@
//go:build !embed
package ui
import "embed"
var staticContent embed.FS

View File

@ -4,88 +4,9 @@ package ui
import ( import (
"embed" "embed"
"io"
"io/fs"
"net/http"
"path/filepath"
"github.com/sirupsen/logrus"
) )
// content holds our static web server content. // content holds our static web server content.
// //
//go:embed all:ui/* //go:embed all:ui/*
var staticContent embed.FS var staticContent embed.FS
type fsFunc func(name string) (fs.File, error)
func (f fsFunc) Open(name string) (fs.File, error) {
return f(name)
}
func pathExist(path string) bool {
_, err := staticContent.Open(path)
return err == nil
}
func openFile(path string) (fs.File, error) {
file, err := staticContent.Open(path)
if err != nil {
logrus.Errorf("openEmbedFile %s err: %v", path, err)
}
return file, err
}
func serveEmbed(basePaths ...string) http.Handler {
handler := fsFunc(func(name string) (fs.File, error) {
logrus.Debugf("serveEmbed name: %s", name)
assetPath := joinEmbedFilepath(append(basePaths, name)...)
logrus.Debugf("serveEmbed final path: %s", assetPath)
return openFile(assetPath)
})
return http.FileServer(http.FS(handler))
}
func serveEmbedIndex(basePath string) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
path := joinEmbedFilepath(basePath, "dashboard", "index.html")
logrus.Debugf("serveEmbedIndex : %s", path)
f, _ := staticContent.Open(path)
io.Copy(rw, f)
f.Close()
})
}
func (u *Handler) ServeAsset() http.Handler {
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
serveEmbed(u.pathSetting()).ServeHTTP(rw, req)
}))
}
func (u *Handler) ServeFaviconDashboard() http.Handler {
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
serveEmbed(u.pathSetting(), "dashboard").ServeHTTP(rw, req)
}))
}
func (u *Handler) IndexFileOnNotFound() http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
path := joinEmbedFilepath(u.pathSetting(), req.URL.Path)
if pathExist(path) {
u.ServeAsset().ServeHTTP(rw, req)
} else {
u.IndexFile().ServeHTTP(rw, req)
}
})
}
func (u *Handler) IndexFile() http.Handler {
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
serveEmbedIndex(u.pathSetting()).ServeHTTP(rw, req)
}))
}
func joinEmbedFilepath(paths ...string) string {
return filepath.ToSlash(filepath.Join(paths...))
}

View File

@ -1,42 +0,0 @@
//go:build !embed
package ui
import (
"net/http"
"os"
"path/filepath"
)
func (u *Handler) ServeAsset() http.Handler {
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
http.FileServer(http.Dir(u.pathSetting())).ServeHTTP(rw, req)
}))
}
func (u *Handler) ServeFaviconDashboard() http.Handler {
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
http.FileServer(http.Dir(filepath.Join(u.pathSetting(), "dashboard"))).ServeHTTP(rw, req)
}))
}
func (u *Handler) IndexFileOnNotFound() http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// we ignore directories here because we want those to come from the CDN when running in that mode
if stat, err := os.Stat(filepath.Join(u.pathSetting(), req.URL.Path)); err == nil && !stat.IsDir() {
u.ServeAsset().ServeHTTP(rw, req)
} else {
u.IndexFile().ServeHTTP(rw, req)
}
})
}
func (u *Handler) IndexFile() http.Handler {
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if path, isURL := u.path(); isURL {
_ = serveIndex(rw, path)
} else {
http.ServeFile(rw, req, filepath.Join(path, "index.html"))
}
}))
}

View File

@ -1,43 +1,30 @@
package ui package ui
import ( import (
"crypto/tls"
"io"
"net/http" "net/http"
"sync"
"github.com/cnrancher/kube-explorer/internal/ui/content"
"github.com/rancher/apiserver/pkg/middleware" "github.com/rancher/apiserver/pkg/middleware"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const (
defaultPath = "./ui"
)
var (
insecureClient = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
)
type StringSetting func() string type StringSetting func() string
type BoolSetting func() bool type BoolSetting func() bool
func StaticSetting[T any](input T) func() T {
return func() T {
return input
}
}
type Handler struct { type Handler struct {
contentHandlers map[string]content.Handler
pathSetting func() string pathSetting func() string
indexSetting func() string indexSetting func() string
releaseSetting func() bool releaseSetting func() bool
offlineSetting func() string offlineSetting func() string
middleware func(http.Handler) http.Handler middleware func(http.Handler) http.Handler
indexMiddleware func(http.Handler) http.Handler indexMiddleware func(http.Handler) http.Handler
downloadOnce sync.Once
downloadSuccess bool
} }
type Options struct { type Options struct {
@ -45,7 +32,7 @@ type Options struct {
Path StringSetting Path StringSetting
// The HTTP URL of the index file to download // The HTTP URL of the index file to download
Index StringSetting Index StringSetting
// Whether or not to run the UI offline, should return true/false/dynamic // Whether or not to run the UI offline, should return true/false/dynamic/embed
Offline StringSetting Offline StringSetting
// Whether or not is it release, if true UI will run offline if set to dynamic // Whether or not is it release, if true UI will run offline if set to dynamic
ReleaseSetting BoolSetting ReleaseSetting BoolSetting
@ -57,6 +44,7 @@ func NewUIHandler(opts *Options) *Handler {
} }
h := &Handler{ h := &Handler{
contentHandlers: make(map[string]content.Handler),
indexSetting: opts.Index, indexSetting: opts.Index,
offlineSetting: opts.Offline, offlineSetting: opts.Offline,
pathSetting: opts.Path, pathSetting: opts.Path,
@ -75,67 +63,76 @@ func NewUIHandler(opts *Options) *Handler {
} }
if h.indexSetting == nil { if h.indexSetting == nil {
h.indexSetting = func() string { h.indexSetting = StaticSetting("")
return "https://releases.rancher.com/dashboard/latest/index.html"
}
} }
if h.offlineSetting == nil { if h.offlineSetting == nil {
h.offlineSetting = func() string { h.offlineSetting = StaticSetting("dynamic")
return "dynamic"
}
} }
if h.pathSetting == nil { if h.pathSetting == nil {
h.pathSetting = func() string { h.pathSetting = StaticSetting("")
return defaultPath
}
} }
if h.releaseSetting == nil { if h.releaseSetting == nil {
h.releaseSetting = func() bool { h.releaseSetting = StaticSetting(false)
return false
}
} }
h.contentHandlers["embed"] = content.NewEmbedded(staticContent, "ui")
h.contentHandlers["false"] = content.NewExternal(h.indexSetting)
h.contentHandlers["true"] = content.NewFilepath(h.pathSetting)
return h return h
} }
func (u *Handler) path() (path string, isURL bool) { func (h *Handler) content() content.Handler {
switch u.offlineSetting() { offline := h.offlineSetting()
case "dynamic": if handler, ok := h.contentHandlers[offline]; ok {
if u.releaseSetting() { return handler
return u.pathSetting(), false
} }
if u.canDownload(u.indexSetting()) { embedHandler := h.contentHandlers["embed"]
return u.indexSetting(), true filepathHandler := h.contentHandlers["true"]
externalHandler := h.contentHandlers["false"]
// default to dynamic
switch {
case h.pathSetting() != "":
if _, err := filepathHandler.GetIndex(); err == nil {
return filepathHandler
} }
return u.pathSetting(), false fallthrough
case "true": case h.releaseSetting():
return u.pathSetting(), false // release must use embed first
return embedHandler
default: default:
return u.indexSetting(), true // try embed
if _, err := embedHandler.GetIndex(); err == nil {
return embedHandler
}
return externalHandler
} }
} }
func (u *Handler) canDownload(url string) bool { func (h *Handler) ServeAssets(next http.Handler) http.Handler {
u.downloadOnce.Do(func() { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := serveIndex(io.Discard, url); err == nil { h.content().ServeAssets(h.middleware, next).ServeHTTP(w, r)
u.downloadSuccess = true
} else {
logrus.Errorf("Failed to download %s, falling back to packaged UI", url)
}
}) })
return u.downloadSuccess
} }
func serveIndex(resp io.Writer, url string) error { func (h *Handler) ServeFaviconDashboard() http.Handler {
r, err := insecureClient.Get(url) return h.middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.content().ServeFaviconDashboard().ServeHTTP(w, r)
}))
}
func (h *Handler) IndexFile() http.Handler {
return h.indexMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rtn, err := h.content().GetIndex()
if err != nil { if err != nil {
return err logrus.Warnf("failed to serve index with error %v", err)
http.NotFoundHandler().ServeHTTP(w, r)
return
} }
defer r.Body.Close() w.WriteHeader(http.StatusOK)
_, _ = w.Write(rtn)
_, err = io.Copy(resp, r.Body) }))
return err
} }

View File

@ -4,29 +4,11 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/cnrancher/kube-explorer/internal/version"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
func New(path string) http.Handler { func New(opt *Options) (http.Handler, APIUI) {
vue := NewUIHandler(&Options{ vue := NewUIHandler(opt)
Path: func() string {
if path == "" {
return defaultPath
}
return path
},
Offline: func() string {
if path != "" {
return "true"
}
return "dynamic"
},
ReleaseSetting: func() bool {
return version.IsRelease()
},
})
router := mux.NewRouter() router := mux.NewRouter()
router.UseEncodedPath() router.UseEncodedPath()
@ -35,7 +17,8 @@ func New(path string) http.Handler {
router.Handle("/dashboard/", vue.IndexFile()) router.Handle("/dashboard/", vue.IndexFile())
router.Handle("/favicon.png", vue.ServeFaviconDashboard()) router.Handle("/favicon.png", vue.ServeFaviconDashboard())
router.Handle("/favicon.ico", vue.ServeFaviconDashboard()) router.Handle("/favicon.ico", vue.ServeFaviconDashboard())
router.PathPrefix("/dashboard/").Handler(vue.IndexFileOnNotFound()) router.PathPrefix("/dashboard/").Handler(vue.ServeAssets(vue.IndexFile()))
router.PathPrefix("/api-ui/").Handler(vue.ServeAssets(http.NotFoundHandler()))
router.PathPrefix("/k8s/clusters/local").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { router.PathPrefix("/k8s/clusters/local").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
url := strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local") url := strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local")
if url == "" { if url == "" {
@ -44,5 +27,5 @@ func New(path string) http.Handler {
http.Redirect(rw, req, url, http.StatusFound) http.Redirect(rw, req, url, http.StatusFound)
}) })
return router return router, apiUI(opt)
} }

15
main.go
View File

@ -14,19 +14,14 @@ import (
"github.com/cnrancher/kube-explorer/internal/server" "github.com/cnrancher/kube-explorer/internal/server"
) )
var (
config stevecli.Config
debugconfig debug.Config
)
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "kube-explorer" app.Name = "kube-explorer"
app.Version = version.FriendlyVersion() app.Version = version.FriendlyVersion()
app.Usage = "" app.Usage = ""
app.Flags = joinFlags( app.Flags = joinFlags(
stevecli.Flags(&config), stevecli.Flags(&keconfig.Steve),
debug.Flags(&debugconfig), debug.Flags(&keconfig.Debug),
keconfig.Flags(), keconfig.Flags(),
) )
app.Action = run app.Action = run
@ -38,12 +33,12 @@ func main() {
func run(_ *cli.Context) error { func run(_ *cli.Context) error {
ctx := signals.SetupSignalContext() ctx := signals.SetupSignalContext()
debugconfig.MustSetupDebug() keconfig.Debug.MustSetupDebug()
s, err := server.ToServer(ctx, &config, false) s, err := server.ToServer(ctx, &keconfig.Steve, false)
if err != nil { if err != nil {
return err return err
} }
return s.ListenAndServe(ctx, config.HTTPSListenPort, config.HTTPListenPort, nil) return s.ListenAndServe(ctx, keconfig.Steve.HTTPSListenPort, keconfig.Steve.HTTPListenPort, nil)
} }
func joinFlags(flags ...[]cli.Flag) []cli.Flag { func joinFlags(flags ...[]cli.Flag) []cli.Flag {

View File

@ -11,7 +11,8 @@ OS_ARCH_ARG_DARWIN="amd64 arm64"
OS_ARCH_ARG_WINDOWS="amd64" OS_ARCH_ARG_WINDOWS="amd64"
LD_INJECT_VALUES="-X github.com/cnrancher/kube-explorer/internal/version.Version=$VERSION LD_INJECT_VALUES="-X github.com/cnrancher/kube-explorer/internal/version.Version=$VERSION
-X github.com/cnrancher/kube-explorer/internal/version.GitCommit=$COMMIT" -X github.com/cnrancher/kube-explorer/internal/version.GitCommit=$COMMIT
-X github.com/cnrancher/kube-explorer/internal/config.APIUIVersion=$CATTLE_API_UI_VERSION"
[ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s" [ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s"

View File

@ -10,9 +10,13 @@ else
TAR_CMD="tar" TAR_CMD="tar"
fi fi
rm -rf internal/ui/ui/*
mkdir -p internal/ui/ui/dashboard mkdir -p internal/ui/ui/dashboard
cd internal/ui/ui/dashboard || exit 1; cd internal/ui/ui/dashboard || exit 1;
curl -sL https://pandaria-dashboard-ui.s3.ap-southeast-2.amazonaws.com/release-2.8-cn/kube-explorer-ui/${CATTLE_DASHBOARD_UI_VERSION}.tar.gz | $TAR_CMD xvzf - --strip-components=2 curl -sL https://pandaria-dashboard-ui.s3.ap-southeast-2.amazonaws.com/release-2.8-cn/kube-explorer-ui/${CATTLE_DASHBOARD_UI_VERSION}.tar.gz | $TAR_CMD xvzf - --strip-components=2
cp index.html ../index.html cp index.html ../index.html
mkdir ../api-ui
cd ../api-ui || exit 1;
curl -sL https://releases.rancher.com/api-ui/${CATTLE_API_UI_VERSION}.tar.gz | $TAR_CMD xvzf - --strip-components=1