1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-22 11:58:18 +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/pkg/errors v0.9.1
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/kubernetes-provider-detector v0.1.5
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/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
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.2/go.mod h1:9b/n58YT7S8bMFEyr1v7xzL72qwZKQQJK2Ir6lMT8Yk=
github.com/rancher/apiserver v0.7.3 h1:a9yibIRiZe2kQ5X+x5cXgajff8S9j3sTu7+kxT6g4hs=
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/go.mod h1:Q2YA42xp7Xc69JiSlJ8GpvLvze261T0iQ/TP4RdMCYk=
github.com/rancher/kubernetes-provider-detector v0.1.5 h1:hWRAsWuJOemzGjz/XrbTlM7QmfO4OedvFE3QwXiH60I=

View File

@@ -1,11 +1,13 @@
package ui
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"github.com/rancher/apiserver/pkg/middleware"
@@ -20,12 +22,13 @@ type StringSetting func() string
type BoolSetting func() bool
type Handler struct {
pathSetting func() string
indexSetting func() string
releaseSetting func() bool
offlineSetting func() string
middleware func(http.Handler) http.Handler
indexMiddleware func(http.Handler) http.Handler
pathSetting func() string
indexSetting func() string
releaseSetting func() bool
offlineSetting func() string
apiUIVersionSetting func() string
middleware func(http.Handler) http.Handler
indexMiddleware func(http.Handler) http.Handler
downloadOnce sync.Once
downloadSuccess bool
@@ -40,6 +43,8 @@ type Options struct {
Offline StringSetting
// Whether or not is it release, if true UI will run offline if set to dynamic
ReleaseSetting BoolSetting
// The version of API UI to use
APIUIVersionSetting StringSetting
}
func NewUIHandler(opts *Options) *Handler {
@@ -48,10 +53,11 @@ func NewUIHandler(opts *Options) *Handler {
}
h := &Handler{
indexSetting: opts.Index,
offlineSetting: opts.Offline,
pathSetting: opts.Path,
releaseSetting: opts.ReleaseSetting,
indexSetting: opts.Index,
offlineSetting: opts.Offline,
pathSetting: opts.Path,
releaseSetting: opts.ReleaseSetting,
apiUIVersionSetting: opts.APIUIVersionSetting,
middleware: middleware.Chain{
middleware.Gzip,
middleware.FrameOptions,
@@ -94,7 +100,7 @@ func NewUIHandler(opts *Options) *Handler {
func (u *Handler) canDownload(url string) bool {
u.downloadOnce.Do(func() {
if err := serveIndex(ioutil.Discard, url); err == nil {
if err := serveRemote(ioutil.Discard, url); err == nil {
u.downloadSuccess = true
} else {
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
}
func (u *Handler) path() (path string, isURL bool) {
func (u *Handler) indexPath() (path string, isURL bool) {
switch u.offlineSetting() {
case "dynamic":
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 {
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
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 {
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if path, isURL := u.path(); isURL {
err := serveIndex(rw, path)
if path, isURL := u.indexPath(); isURL {
err := serveRemote(rw, path)
if err != nil {
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{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
@@ -168,6 +214,15 @@ func serveIndex(resp io.Writer, url string) error {
}
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)
return err
}