Files
kubesphere/kube/pkg/openapi/downloader.go
2025-04-30 15:53:51 +08:00

131 lines
2.9 KiB
Go

/*
* Copyright 2024 the KubeSphere Authors.
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package openapi
import (
"fmt"
"net/http"
"time"
)
const (
specDownloadTimeout = time.Minute
)
type CacheableDownloader interface {
Name() string
GetV2() ([]byte, error)
GetV3() ([]byte, error)
UpdateDownloader(apiService ApiService) error
}
type Downloader struct{}
func NewDownloader() *Downloader {
return &Downloader{}
}
func (s *Downloader) Download(handler http.Handler, req *http.Request) (data []byte, err error) {
handler = http.TimeoutHandler(handler, specDownloadTimeout, "request timed out")
writer := newInMemoryResponseWriter()
handler.ServeHTTP(writer, req)
switch writer.respCode {
case http.StatusNotModified:
return nil, nil
case http.StatusNotFound:
return nil, ErrAPIServiceNotFound
case http.StatusOK:
return writer.data, nil
default:
return nil, fmt.Errorf("failed to retrieve openAPI spec, http error: %s", writer.String())
}
}
type inMemoryResponseWriter struct {
writeHeaderCalled bool
header http.Header
respCode int
data []byte
}
func newInMemoryResponseWriter() *inMemoryResponseWriter {
return &inMemoryResponseWriter{header: http.Header{}}
}
func (r *inMemoryResponseWriter) Header() http.Header {
return r.header
}
func (r *inMemoryResponseWriter) WriteHeader(code int) {
r.writeHeaderCalled = true
r.respCode = code
}
func (r *inMemoryResponseWriter) Write(in []byte) (int, error) {
if !r.writeHeaderCalled {
r.WriteHeader(http.StatusOK)
}
r.data = append(r.data, in...)
return len(in), nil
}
func (r *inMemoryResponseWriter) String() string {
s := fmt.Sprintf("ResponseCode: %d", r.respCode)
if r.data != nil {
s += fmt.Sprintf(", Body: %s", string(r.data))
}
if r.header != nil {
s += fmt.Sprintf(", Header: %s", r.header)
}
return s
}
type cacheableDownloader struct {
apiService ApiService
downloader *Downloader
}
func NewCacheableDownloader(apiService ApiService, downloader *Downloader) (CacheableDownloader, error) {
c := &cacheableDownloader{
apiService: apiService,
downloader: downloader,
}
if err := c.apiService.UpdateAPIService(); err != nil {
return nil, err
}
return c, nil
}
func (c *cacheableDownloader) Name() string {
return c.apiService.Name()
}
func (c *cacheableDownloader) Get(url string) ([]byte, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/json")
return c.downloader.Download(c.apiService, req)
}
func (c *cacheableDownloader) GetV2() ([]byte, error) {
return c.Get("/openapi/v2")
}
func (c *cacheableDownloader) GetV3() ([]byte, error) {
return c.Get("/openapi/v3")
}
func (c *cacheableDownloader) UpdateDownloader(apiService ApiService) error {
c.apiService = apiService
return c.apiService.UpdateAPIService()
}