diff --git a/Dockerfile.dapper b/Dockerfile.dapper index 4bc4a64..820ee17 100644 --- a/Dockerfile.dapper +++ b/Dockerfile.dapper @@ -20,7 +20,8 @@ RUN if [ "${ARCH}" == "amd64" ]; then \ fi COPY --from=tools /app/release-notary /usr/local/bin/ 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_SOURCE /go/src/github.com/cnrancher/kube-explorer ENV DAPPER_OUTPUT ./bin ./dist diff --git a/internal/config/flags.go b/internal/config/flags.go index 843aa21..f972131 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -6,6 +6,7 @@ import ( var InsecureSkipTLSVerify bool var SystemDefaultRegistry string +var APIUIVersion = "1.1.11" var ShellPodImage string @@ -24,5 +25,11 @@ func Flags() []cli.Flag { Destination: &ShellPodImage, Value: "rancher/shell:v0.2.1-rc.7", }, + cli.StringFlag{ + Name: "apiui-version", + Hidden: true, + Destination: &APIUIVersion, + Value: APIUIVersion, + }, } } diff --git a/internal/config/steve.go b/internal/config/steve.go new file mode 100644 index 0000000..ef7d5e1 --- /dev/null +++ b/internal/config/steve.go @@ -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 +) diff --git a/internal/server/config.go b/internal/server/config.go index 5d1790a..cfcee56 100644 --- a/internal/server/config.go +++ b/internal/server/config.go @@ -17,6 +17,7 @@ import ( "github.com/cnrancher/kube-explorer/internal/config" "github.com/cnrancher/kube-explorer/internal/resources/cluster" "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) { @@ -48,10 +49,15 @@ func ToServer(ctx context.Context, c *cli.Config, sqlCache bool) (*server.Server 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{ AuthMiddleware: auth, Controllers: controllers, - Next: ui.New(c.UIPath), + Next: ui, SQLCache: sqlCache, // router needs to hack here 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 } + steveServer.APIServer.CustomAPIUIResponseWriter(apiui.CSS(), apiui.JS(), func() string { return config.APIUIVersion }) + // registrer local cluster if err := cluster.Register(ctx, steveServer, c.Context); err != nil { return steveServer, err diff --git a/internal/ui/apiui.go b/internal/ui/apiui.go new file mode 100644 index 0000000..3783007 --- /dev/null +++ b/internal/ui/apiui.go @@ -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") +} diff --git a/internal/ui/content/content.go b/internal/ui/content/content.go new file mode 100644 index 0000000..e9ac769 --- /dev/null +++ b/internal/ui/content/content.go @@ -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() +} diff --git a/internal/ui/content/external.go b/internal/ui/content/external.go new file mode 100644 index 0000000..cb23213 --- /dev/null +++ b/internal/ui/content/external.go @@ -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() +} diff --git a/internal/ui/content/fs.go b/internal/ui/content/fs.go new file mode 100644 index 0000000..3df31d0 --- /dev/null +++ b/internal/ui/content/fs.go @@ -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) +} diff --git a/internal/ui/content/fs_embed.go b/internal/ui/content/fs_embed.go new file mode 100644 index 0000000..b0720c0 --- /dev/null +++ b/internal/ui/content/fs_embed.go @@ -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...)) +} diff --git a/internal/ui/content/fs_filepath.go b/internal/ui/content/fs_filepath.go new file mode 100644 index 0000000..9d85d46 --- /dev/null +++ b/internal/ui/content/fs_filepath.go @@ -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 +} diff --git a/internal/ui/dev.go b/internal/ui/dev.go new file mode 100644 index 0000000..2696e8e --- /dev/null +++ b/internal/ui/dev.go @@ -0,0 +1,7 @@ +//go:build !embed + +package ui + +import "embed" + +var staticContent embed.FS diff --git a/internal/ui/embed.go b/internal/ui/embed.go index fdaf110..eff18b6 100644 --- a/internal/ui/embed.go +++ b/internal/ui/embed.go @@ -4,88 +4,9 @@ package ui import ( "embed" - "io" - "io/fs" - "net/http" - "path/filepath" - - "github.com/sirupsen/logrus" ) // content holds our static web server content. // //go:embed all:ui/* 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...)) -} diff --git a/internal/ui/external.go b/internal/ui/external.go deleted file mode 100644 index 676d238..0000000 --- a/internal/ui/external.go +++ /dev/null @@ -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")) - } - })) -} diff --git a/internal/ui/handler.go b/internal/ui/handler.go index 343039a..463583f 100644 --- a/internal/ui/handler.go +++ b/internal/ui/handler.go @@ -1,43 +1,30 @@ package ui import ( - "crypto/tls" - "io" "net/http" - "sync" + "github.com/cnrancher/kube-explorer/internal/ui/content" "github.com/rancher/apiserver/pkg/middleware" "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 BoolSetting func() bool +func StaticSetting[T any](input T) func() T { + return func() T { + return input + } +} + type Handler struct { + contentHandlers map[string]content.Handler pathSetting func() string indexSetting func() string releaseSetting func() bool offlineSetting func() string middleware func(http.Handler) http.Handler indexMiddleware func(http.Handler) http.Handler - - downloadOnce sync.Once - downloadSuccess bool } type Options struct { @@ -45,7 +32,7 @@ type Options struct { Path StringSetting // The HTTP URL of the index file to download 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 // Whether or not is it release, if true UI will run offline if set to dynamic ReleaseSetting BoolSetting @@ -57,10 +44,11 @@ func NewUIHandler(opts *Options) *Handler { } h := &Handler{ - indexSetting: opts.Index, - offlineSetting: opts.Offline, - pathSetting: opts.Path, - releaseSetting: opts.ReleaseSetting, + contentHandlers: make(map[string]content.Handler), + indexSetting: opts.Index, + offlineSetting: opts.Offline, + pathSetting: opts.Path, + releaseSetting: opts.ReleaseSetting, middleware: middleware.Chain{ middleware.Gzip, middleware.FrameOptions, @@ -75,67 +63,76 @@ func NewUIHandler(opts *Options) *Handler { } if h.indexSetting == nil { - h.indexSetting = func() string { - return "https://releases.rancher.com/dashboard/latest/index.html" - } + h.indexSetting = StaticSetting("") } if h.offlineSetting == nil { - h.offlineSetting = func() string { - return "dynamic" - } + h.offlineSetting = StaticSetting("dynamic") } if h.pathSetting == nil { - h.pathSetting = func() string { - return defaultPath - } + h.pathSetting = StaticSetting("") } if h.releaseSetting == nil { - h.releaseSetting = func() bool { - return false - } + h.releaseSetting = StaticSetting(false) } + h.contentHandlers["embed"] = content.NewEmbedded(staticContent, "ui") + h.contentHandlers["false"] = content.NewExternal(h.indexSetting) + h.contentHandlers["true"] = content.NewFilepath(h.pathSetting) + return h } -func (u *Handler) path() (path string, isURL bool) { - switch u.offlineSetting() { - case "dynamic": - if u.releaseSetting() { - return u.pathSetting(), false +func (h *Handler) content() content.Handler { + offline := h.offlineSetting() + if handler, ok := h.contentHandlers[offline]; ok { + return handler + } + embedHandler := h.contentHandlers["embed"] + filepathHandler := h.contentHandlers["true"] + externalHandler := h.contentHandlers["false"] + // default to dynamic + switch { + case h.pathSetting() != "": + if _, err := filepathHandler.GetIndex(); err == nil { + return filepathHandler } - if u.canDownload(u.indexSetting()) { - return u.indexSetting(), true - } - return u.pathSetting(), false - case "true": - return u.pathSetting(), false + fallthrough + case h.releaseSetting(): + // release must use embed first + return embedHandler default: - return u.indexSetting(), true - } -} - -func (u *Handler) canDownload(url string) bool { - u.downloadOnce.Do(func() { - if err := serveIndex(io.Discard, url); err == nil { - u.downloadSuccess = true - } else { - logrus.Errorf("Failed to download %s, falling back to packaged UI", url) + // try embed + if _, err := embedHandler.GetIndex(); err == nil { + return embedHandler } - }) - return u.downloadSuccess -} - -func serveIndex(resp io.Writer, url string) error { - r, err := insecureClient.Get(url) - if err != nil { - return err + return externalHandler } - defer r.Body.Close() - - _, err = io.Copy(resp, r.Body) - return err +} + +func (h *Handler) ServeAssets(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h.content().ServeAssets(h.middleware, next).ServeHTTP(w, r) + }) +} + +func (h *Handler) ServeFaviconDashboard() http.Handler { + 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 { + logrus.Warnf("failed to serve index with error %v", err) + http.NotFoundHandler().ServeHTTP(w, r) + return + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write(rtn) + })) } diff --git a/internal/ui/routers.go b/internal/ui/routers.go index eb1f499..fe5a98b 100644 --- a/internal/ui/routers.go +++ b/internal/ui/routers.go @@ -4,29 +4,11 @@ import ( "net/http" "strings" - "github.com/cnrancher/kube-explorer/internal/version" "github.com/gorilla/mux" ) -func New(path string) http.Handler { - vue := NewUIHandler(&Options{ - Path: func() string { - if path == "" { - return defaultPath - } - return path - }, - Offline: func() string { - if path != "" { - return "true" - } - return "dynamic" - }, - ReleaseSetting: func() bool { - return version.IsRelease() - }, - }) - +func New(opt *Options) (http.Handler, APIUI) { + vue := NewUIHandler(opt) router := mux.NewRouter() router.UseEncodedPath() @@ -35,7 +17,8 @@ func New(path string) http.Handler { router.Handle("/dashboard/", vue.IndexFile()) router.Handle("/favicon.png", 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) { url := strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local") if url == "" { @@ -44,5 +27,5 @@ func New(path string) http.Handler { http.Redirect(rw, req, url, http.StatusFound) }) - return router + return router, apiUI(opt) } diff --git a/main.go b/main.go index 7f86f30..089d3c7 100644 --- a/main.go +++ b/main.go @@ -14,19 +14,14 @@ import ( "github.com/cnrancher/kube-explorer/internal/server" ) -var ( - config stevecli.Config - debugconfig debug.Config -) - func main() { app := cli.NewApp() app.Name = "kube-explorer" app.Version = version.FriendlyVersion() app.Usage = "" app.Flags = joinFlags( - stevecli.Flags(&config), - debug.Flags(&debugconfig), + stevecli.Flags(&keconfig.Steve), + debug.Flags(&keconfig.Debug), keconfig.Flags(), ) app.Action = run @@ -38,12 +33,12 @@ func main() { func run(_ *cli.Context) error { ctx := signals.SetupSignalContext() - debugconfig.MustSetupDebug() - s, err := server.ToServer(ctx, &config, false) + keconfig.Debug.MustSetupDebug() + s, err := server.ToServer(ctx, &keconfig.Steve, false) if err != nil { 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 { diff --git a/scripts/build b/scripts/build index 6d7fb8b..4999195 100755 --- a/scripts/build +++ b/scripts/build @@ -11,7 +11,8 @@ OS_ARCH_ARG_DARWIN="amd64 arm64" OS_ARCH_ARG_WINDOWS="amd64" 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" diff --git a/scripts/download b/scripts/download index 3fb25ea..ed1ae4c 100755 --- a/scripts/download +++ b/scripts/download @@ -10,9 +10,13 @@ else TAR_CMD="tar" fi +rm -rf internal/ui/ui/* + mkdir -p internal/ui/ui/dashboard 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 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