1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-23 20:38:55 +00:00

Add functions ServeAPIUI apiUIPath for api ui assets (#806)

This commit is contained in:
Sakala Venkata Krishna Rohit
2025-09-11 11:52:32 -07:00
committed by GitHub
parent 13d5ad3ccb
commit 0c6dd58fc5
3 changed files with 73 additions and 18 deletions

2
go.mod
View File

@@ -20,7 +20,7 @@ require (
github.com/pborman/uuid v1.2.1 github.com/pborman/uuid v1.2.1
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.22.0 github.com/prometheus/client_golang v1.22.0
github.com/rancher/apiserver v0.7.2 github.com/rancher/apiserver v0.7.3
github.com/rancher/dynamiclistener v0.7.0 github.com/rancher/dynamiclistener v0.7.0
github.com/rancher/kubernetes-provider-detector v0.1.5 github.com/rancher/kubernetes-provider-detector v0.1.5
github.com/rancher/lasso v0.2.3 github.com/rancher/lasso v0.2.3

4
go.sum
View File

@@ -214,8 +214,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rancher/apiserver v0.7.2 h1:mDgVHgDF0DJ2AmN6KGzdlk1ueAJJJGYOrJhXG+1LNn8= github.com/rancher/apiserver v0.7.3 h1:a9yibIRiZe2kQ5X+x5cXgajff8S9j3sTu7+kxT6g4hs=
github.com/rancher/apiserver v0.7.2/go.mod h1:9b/n58YT7S8bMFEyr1v7xzL72qwZKQQJK2Ir6lMT8Yk= github.com/rancher/apiserver v0.7.3/go.mod h1:9b/n58YT7S8bMFEyr1v7xzL72qwZKQQJK2Ir6lMT8Yk=
github.com/rancher/dynamiclistener v0.7.0 h1:+jyfZ4lVamc1UbKWo8V8dhSPtCgRZYaY8nm7wiHeko4= github.com/rancher/dynamiclistener v0.7.0 h1:+jyfZ4lVamc1UbKWo8V8dhSPtCgRZYaY8nm7wiHeko4=
github.com/rancher/dynamiclistener v0.7.0/go.mod h1:Q2YA42xp7Xc69JiSlJ8GpvLvze261T0iQ/TP4RdMCYk= github.com/rancher/dynamiclistener v0.7.0/go.mod h1:Q2YA42xp7Xc69JiSlJ8GpvLvze261T0iQ/TP4RdMCYk=
github.com/rancher/kubernetes-provider-detector v0.1.5 h1:hWRAsWuJOemzGjz/XrbTlM7QmfO4OedvFE3QwXiH60I= github.com/rancher/kubernetes-provider-detector v0.1.5 h1:hWRAsWuJOemzGjz/XrbTlM7QmfO4OedvFE3QwXiH60I=

View File

@@ -1,11 +1,13 @@
package ui package ui
import ( import (
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"sync" "sync"
"github.com/rancher/apiserver/pkg/middleware" "github.com/rancher/apiserver/pkg/middleware"
@@ -24,6 +26,7 @@ type Handler struct {
indexSetting func() string indexSetting func() string
releaseSetting func() bool releaseSetting func() bool
offlineSetting func() string offlineSetting func() string
apiUIVersionSetting 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
@@ -40,6 +43,8 @@ type Options struct {
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
// The version of API UI to use
APIUIVersionSetting StringSetting
} }
func NewUIHandler(opts *Options) *Handler { func NewUIHandler(opts *Options) *Handler {
@@ -52,6 +57,7 @@ func NewUIHandler(opts *Options) *Handler {
offlineSetting: opts.Offline, offlineSetting: opts.Offline,
pathSetting: opts.Path, pathSetting: opts.Path,
releaseSetting: opts.ReleaseSetting, releaseSetting: opts.ReleaseSetting,
apiUIVersionSetting: opts.APIUIVersionSetting,
middleware: middleware.Chain{ middleware: middleware.Chain{
middleware.Gzip, middleware.Gzip,
middleware.FrameOptions, middleware.FrameOptions,
@@ -94,7 +100,7 @@ func NewUIHandler(opts *Options) *Handler {
func (u *Handler) canDownload(url string) bool { func (u *Handler) canDownload(url string) bool {
u.downloadOnce.Do(func() { u.downloadOnce.Do(func() {
if err := serveIndex(ioutil.Discard, url); err == nil { if err := serveRemote(ioutil.Discard, url); err == nil {
u.downloadSuccess = true u.downloadSuccess = true
} else { } else {
logrus.Errorf("Failed to download %s, falling back to packaged UI", url) logrus.Errorf("Failed to download %s, falling back to packaged UI", url)
@@ -103,7 +109,7 @@ func (u *Handler) canDownload(url string) bool {
return u.downloadSuccess return u.downloadSuccess
} }
func (u *Handler) path() (path string, isURL bool) { func (u *Handler) indexPath() (path string, isURL bool) {
switch u.offlineSetting() { switch u.offlineSetting() {
case "dynamic": case "dynamic":
if u.releaseSetting() { if u.releaseSetting() {
@@ -120,6 +126,46 @@ func (u *Handler) path() (path string, isURL bool) {
} }
} }
func (u *Handler) apiUIPath() (string, bool) {
version := u.apiUIVersionSetting()
remotePath := "https://releases.rancher.com/api-ui/"
switch u.offlineSetting() {
case "dynamic":
if u.releaseSetting() {
return filepath.Join(u.pathSetting(), "api-ui"), false
}
if u.canDownload(fmt.Sprintf("%s/%s/%s", remotePath, version, "ui.min.js")) {
return remotePath, true
}
return filepath.Join(u.pathSetting(), "api-ui"), false
case "true":
return filepath.Join(u.pathSetting(), "api-ui"), false
default:
return remotePath, true
}
}
func (u *Handler) ServeAPIUI() http.Handler {
return u.middleware(http.StripPrefix("/api-ui/",
http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
base, isURL := u.apiUIPath()
if isURL {
remoteURL := base + req.URL.Path
if err := serveRemote(rw, remoteURL); err != nil {
logrus.Errorf("failed to fetch asset from %s: %v", remoteURL, err)
}
} else {
parts := strings.SplitN(req.URL.Path, "/", 2)
if len(parts) == 2 {
http.ServeFile(rw, req, filepath.Join(base, parts[1]))
} else {
http.NotFound(rw, req)
}
}
})))
}
func (u *Handler) ServeAsset() http.Handler { func (u *Handler) ServeAsset() http.Handler {
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
http.FileServer(http.Dir(u.pathSetting())).ServeHTTP(rw, req) http.FileServer(http.Dir(u.pathSetting())).ServeHTTP(rw, req)
@@ -145,8 +191,8 @@ func (u *Handler) IndexFileOnNotFound() http.Handler {
func (u *Handler) IndexFile() http.Handler { func (u *Handler) IndexFile() http.Handler {
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if path, isURL := u.path(); isURL { if path, isURL := u.indexPath(); isURL {
err := serveIndex(rw, path) err := serveRemote(rw, path)
if err != nil { if err != nil {
logrus.Errorf("failed to download %s: %v", path, err) logrus.Errorf("failed to download %s: %v", path, err)
} }
@@ -156,7 +202,7 @@ func (u *Handler) IndexFile() http.Handler {
})) }))
} }
func serveIndex(resp io.Writer, url string) error { func serveRemote(resp io.Writer, url string) error {
client := &http.Client{ client := &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
@@ -168,6 +214,15 @@ func serveIndex(resp io.Writer, url string) error {
} }
defer r.Body.Close() defer r.Body.Close()
if w, ok := resp.(http.ResponseWriter); ok {
for k, v := range r.Header {
for _, vv := range v {
w.Header().Add(k, vv)
}
}
w.WriteHeader(r.StatusCode)
}
_, err = io.Copy(resp, r.Body) _, err = io.Copy(resp, r.Body)
return err return err
} }