11package ui
22
33import (
4+ "fmt"
45 "io"
56 "io/ioutil"
67 "net/http"
78 "os"
89 "path/filepath"
10+ "strings"
911 "sync"
1012
1113 "github.com/rancher/apiserver/pkg/middleware"
@@ -20,12 +22,13 @@ type StringSetting func() string
2022type BoolSetting func () bool
2123
2224type Handler struct {
23- pathSetting func () string
24- indexSetting func () string
25- releaseSetting func () bool
26- offlineSetting func () string
27- middleware func (http.Handler ) http.Handler
28- indexMiddleware func (http.Handler ) http.Handler
25+ pathSetting func () string
26+ indexSetting func () string
27+ releaseSetting func () bool
28+ offlineSetting func () string
29+ apiUIVersionSetting func () string
30+ middleware func (http.Handler ) http.Handler
31+ indexMiddleware func (http.Handler ) http.Handler
2932
3033 downloadOnce sync.Once
3134 downloadSuccess bool
@@ -40,6 +43,8 @@ type Options struct {
4043 Offline StringSetting
4144 // Whether or not is it release, if true UI will run offline if set to dynamic
4245 ReleaseSetting BoolSetting
46+ // The version of API UI to use
47+ APIUIVersionSetting StringSetting
4348}
4449
4550func NewUIHandler (opts * Options ) * Handler {
@@ -48,10 +53,11 @@ func NewUIHandler(opts *Options) *Handler {
4853 }
4954
5055 h := & Handler {
51- indexSetting : opts .Index ,
52- offlineSetting : opts .Offline ,
53- pathSetting : opts .Path ,
54- releaseSetting : opts .ReleaseSetting ,
56+ indexSetting : opts .Index ,
57+ offlineSetting : opts .Offline ,
58+ pathSetting : opts .Path ,
59+ releaseSetting : opts .ReleaseSetting ,
60+ apiUIVersionSetting : opts .APIUIVersionSetting ,
5561 middleware : middleware.Chain {
5662 middleware .Gzip ,
5763 middleware .FrameOptions ,
@@ -94,7 +100,7 @@ func NewUIHandler(opts *Options) *Handler {
94100
95101func (u * Handler ) canDownload (url string ) bool {
96102 u .downloadOnce .Do (func () {
97- if err := serveIndex (ioutil .Discard , url ); err == nil {
103+ if err := serveRemote (ioutil .Discard , url ); err == nil {
98104 u .downloadSuccess = true
99105 } else {
100106 logrus .Errorf ("Failed to download %s, falling back to packaged UI" , url )
@@ -103,7 +109,7 @@ func (u *Handler) canDownload(url string) bool {
103109 return u .downloadSuccess
104110}
105111
106- func (u * Handler ) path () (path string , isURL bool ) {
112+ func (u * Handler ) indexPath () (path string , isURL bool ) {
107113 switch u .offlineSetting () {
108114 case "dynamic" :
109115 if u .releaseSetting () {
@@ -120,6 +126,46 @@ func (u *Handler) path() (path string, isURL bool) {
120126 }
121127}
122128
129+ func (u * Handler ) apiUIPath () (string , bool ) {
130+ version := u .apiUIVersionSetting ()
131+ remotePath := "https://releases.rancher.com/api-ui/"
132+
133+ switch u .offlineSetting () {
134+ case "dynamic" :
135+ if u .releaseSetting () {
136+ return filepath .Join (u .pathSetting (), "api-ui" ), false
137+ }
138+ if u .canDownload (fmt .Sprintf ("%s/%s/%s" , remotePath , version , "ui.min.js" )) {
139+ return remotePath , true
140+ }
141+ return filepath .Join (u .pathSetting (), "api-ui" ), false
142+ case "true" :
143+ return filepath .Join (u .pathSetting (), "api-ui" ), false
144+ default :
145+ return remotePath , true
146+ }
147+ }
148+
149+ func (u * Handler ) ServeAPIUI () http.Handler {
150+ return u .middleware (http .StripPrefix ("/api-ui/" ,
151+ http .HandlerFunc (func (rw http.ResponseWriter , req * http.Request ) {
152+ base , isURL := u .apiUIPath ()
153+ if isURL {
154+ remoteURL := base + req .URL .Path
155+ if err := serveRemote (rw , remoteURL ); err != nil {
156+ logrus .Errorf ("failed to fetch asset from %s: %v" , remoteURL , err )
157+ }
158+ } else {
159+ parts := strings .SplitN (req .URL .Path , "/" , 2 )
160+ if len (parts ) == 2 {
161+ http .ServeFile (rw , req , filepath .Join (base , parts [1 ]))
162+ } else {
163+ http .NotFound (rw , req )
164+ }
165+ }
166+ })))
167+ }
168+
123169func (u * Handler ) ServeAsset () http.Handler {
124170 return u .middleware (http .HandlerFunc (func (rw http.ResponseWriter , req * http.Request ) {
125171 http .FileServer (http .Dir (u .pathSetting ())).ServeHTTP (rw , req )
@@ -145,8 +191,8 @@ func (u *Handler) IndexFileOnNotFound() http.Handler {
145191
146192func (u * Handler ) IndexFile () http.Handler {
147193 return u .indexMiddleware (http .HandlerFunc (func (rw http.ResponseWriter , req * http.Request ) {
148- if path , isURL := u .path (); isURL {
149- err := serveIndex (rw , path )
194+ if path , isURL := u .indexPath (); isURL {
195+ err := serveRemote (rw , path )
150196 if err != nil {
151197 logrus .Errorf ("failed to download %s: %v" , path , err )
152198 }
@@ -156,7 +202,7 @@ func (u *Handler) IndexFile() http.Handler {
156202 }))
157203}
158204
159- func serveIndex (resp io.Writer , url string ) error {
205+ func serveRemote (resp io.Writer , url string ) error {
160206 client := & http.Client {
161207 Transport : & http.Transport {
162208 Proxy : http .ProxyFromEnvironment ,
@@ -168,6 +214,15 @@ func serveIndex(resp io.Writer, url string) error {
168214 }
169215 defer r .Body .Close ()
170216
217+ if w , ok := resp .(http.ResponseWriter ); ok {
218+ for k , v := range r .Header {
219+ for _ , vv := range v {
220+ w .Header ().Add (k , vv )
221+ }
222+ }
223+ w .WriteHeader (r .StatusCode )
224+ }
225+
171226 _ , err = io .Copy (resp , r .Body )
172227 return err
173228}
0 commit comments