mirror of
https://github.com/goharbor/harbor
synced 2025-04-13 18:38:38 +00:00
Merge pull request #5242 from steven-zou/chart_repo_supporting
Implement the related handler methods of the transparent operations
This commit is contained in:
commit
e1474ac50b
|
@ -2,15 +2,16 @@ package chartserver
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
//BaseHandler defines the handlers related with the chart server itself.
|
||||
type BaseHandler struct {
|
||||
//Proxy used to to transfer the traffic of requests
|
||||
//It's mainly used to talk to the backend chart server
|
||||
trafficProxy *httputil.ReverseProxy
|
||||
trafficProxy *ProxyEngine
|
||||
}
|
||||
|
||||
//GetHealthStatus will return the health status of the backend chart repository server
|
||||
func (bh *BaseHandler) GetHealthStatus(w http.ResponseWriter, req *http.Request) {}
|
||||
func (bh *BaseHandler) GetHealthStatus(w http.ResponseWriter, req *http.Request) {
|
||||
bh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package chartserver
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
|
@ -29,8 +28,8 @@ func NewController(backendServer *url.URL) (*Controller, error) {
|
|||
return nil, errors.New("failed to create chartserver.Controller: backend sever address is required")
|
||||
}
|
||||
|
||||
//Currently, no customization requirements needed, so let's use the simple proxy here now
|
||||
proxy := httputil.NewSingleHostReverseProxy(backendServer)
|
||||
//Use customized reverse proxy
|
||||
proxy := NewProxyEngine(backendServer)
|
||||
|
||||
//Initialize chart operator for use
|
||||
operator := &ChartOperator{}
|
||||
|
|
|
@ -2,7 +2,6 @@ package chartserver
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
//ManipulationHandler includes all the handler methods for the purpose of manipulating the
|
||||
|
@ -10,28 +9,41 @@ import (
|
|||
type ManipulationHandler struct {
|
||||
//Proxy used to to transfer the traffic of requests
|
||||
//It's mainly used to talk to the backend chart server
|
||||
trafficProxy *httputil.ReverseProxy
|
||||
trafficProxy *ProxyEngine
|
||||
|
||||
//Parse and process the chart version to provide required info data
|
||||
chartOperator *ChartOperator
|
||||
}
|
||||
|
||||
//ListCharts lists all the charts under the specified namespace
|
||||
func (mh *ManipulationHandler) ListCharts(w http.ResponseWriter, req *http.Request) {}
|
||||
func (mh *ManipulationHandler) ListCharts(w http.ResponseWriter, req *http.Request) {
|
||||
mh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//GetChart returns all the chart versions under the specified chart
|
||||
func (mh *ManipulationHandler) GetChart(w http.ResponseWriter, req *http.Request) {}
|
||||
func (mh *ManipulationHandler) GetChart(w http.ResponseWriter, req *http.Request) {
|
||||
mh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//GetChartVersion get the specified version for one chart
|
||||
//This handler should return the details of the chart version,
|
||||
//maybe including metadata,dependencies and values etc.
|
||||
func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.Request) {}
|
||||
func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
w.Write([]byte("not implemented"))
|
||||
}
|
||||
|
||||
//UploadChartVersion will save the new version of the chart to the backend storage
|
||||
func (mh *ManipulationHandler) UploadChartVersion(w http.ResponseWriter, req *http.Request) {}
|
||||
func (mh *ManipulationHandler) UploadChartVersion(w http.ResponseWriter, req *http.Request) {
|
||||
mh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//UploadProvenanceFile will save the provenance file of the chart to the backend storage
|
||||
func (mh *ManipulationHandler) UploadProvenanceFile(w http.ResponseWriter, req *http.Request) {}
|
||||
func (mh *ManipulationHandler) UploadProvenanceFile(w http.ResponseWriter, req *http.Request) {
|
||||
mh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//DeleteChartVersion will delete the specified version of the chart
|
||||
func (mh *ManipulationHandler) DeleteChartVersion(w http.ResponseWriter, req *http.Request) {}
|
||||
func (mh *ManipulationHandler) DeleteChartVersion(w http.ResponseWriter, req *http.Request) {
|
||||
mh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package chartserver
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
//RepositoryHandler defines all the handlers to handle the requests related with chart repository
|
||||
|
@ -10,18 +9,24 @@ import (
|
|||
type RepositoryHandler struct {
|
||||
//Proxy used to to transfer the traffic of requests
|
||||
//It's mainly used to talk to the backend chart server
|
||||
trafficProxy *httputil.ReverseProxy
|
||||
trafficProxy *ProxyEngine
|
||||
}
|
||||
|
||||
//GetIndexFileWithNS will read the index.yaml data under the specified namespace
|
||||
func (rh *RepositoryHandler) GetIndexFileWithNS(w http.ResponseWriter, req *http.Request) {
|
||||
rh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//GetIndexFile will read the index.yaml under all namespaces and merge them as a single one
|
||||
//Please be aware that, to support this function, the backend chart repository server should
|
||||
//enable multi-tenancies
|
||||
func (rh *RepositoryHandler) GetIndexFile(w http.ResponseWriter, req *http.Request) {}
|
||||
func (rh *RepositoryHandler) GetIndexFile(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
w.Write([]byte("not implemented"))
|
||||
}
|
||||
|
||||
//DownloadChartObject will download the stored chart object to the client
|
||||
//e.g: helm install
|
||||
func (rh *RepositoryHandler) DownloadChartObject(w http.ResponseWriter, req *http.Request) {}
|
||||
func (rh *RepositoryHandler) DownloadChartObject(w http.ResponseWriter, req *http.Request) {
|
||||
rh.trafficProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
|
115
src/chartserver/reverse_proxy.go
Normal file
115
src/chartserver/reverse_proxy.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package chartserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
userName = "chart_controller"
|
||||
passwordKey = "UI_SECRET"
|
||||
agentHarbor = "HARBOR"
|
||||
authHeader = "Authorization"
|
||||
contentLengthHeader = "Content-Length"
|
||||
)
|
||||
|
||||
//ProxyEngine is used to proxy the related traffics
|
||||
type ProxyEngine struct {
|
||||
//The backend target server the traffic will be forwarded to
|
||||
//Just in case we'll use it
|
||||
backend *url.URL
|
||||
|
||||
//Use go reverse proxy as engine
|
||||
engine *httputil.ReverseProxy
|
||||
}
|
||||
|
||||
//NewProxyEngine is constructor of NewProxyEngine
|
||||
func NewProxyEngine(target *url.URL) *ProxyEngine {
|
||||
return &ProxyEngine{
|
||||
backend: target,
|
||||
engine: &httputil.ReverseProxy{
|
||||
ErrorLog: log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
|
||||
Director: func(req *http.Request) {
|
||||
director(target, req)
|
||||
},
|
||||
ModifyResponse: modifyResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//ServeHTTP serves the incoming http requests
|
||||
func (pe *ProxyEngine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
pe.engine.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//Overwrite the http requests
|
||||
func director(target *url.URL, req *http.Request) {
|
||||
targetQuery := target.RawQuery
|
||||
|
||||
//Overwrite the request URL to the target path
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
|
||||
if targetQuery == "" || req.URL.RawQuery == "" {
|
||||
req.URL.RawQuery = targetQuery + req.URL.RawQuery
|
||||
} else {
|
||||
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
|
||||
}
|
||||
if _, ok := req.Header["User-Agent"]; !ok {
|
||||
req.Header.Set("User-Agent", agentHarbor)
|
||||
}
|
||||
|
||||
//Get the password from the env
|
||||
//Ignore the empty checking, the backend server should return the right status code
|
||||
//with invalid credential
|
||||
password := os.Getenv(passwordKey)
|
||||
|
||||
//Add authentication header
|
||||
req.SetBasicAuth(userName, password)
|
||||
}
|
||||
|
||||
//Modify the http response
|
||||
func modifyResponse(res *http.Response) error {
|
||||
//Detect the 401 code, if it is,
|
||||
//overwrite it to 500.
|
||||
//We also re-write the error content
|
||||
if res.StatusCode == http.StatusUnauthorized {
|
||||
errorObj := make(map[string]string)
|
||||
errorObj["error"] = "operation request from unauthentic source is rejected"
|
||||
content, err := json.Marshal(errorObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size := len(content)
|
||||
body := ioutil.NopCloser(bytes.NewReader(content))
|
||||
res.Body = body
|
||||
res.ContentLength = int64(size)
|
||||
res.Header.Set(contentLengthHeader, strconv.Itoa(size))
|
||||
res.StatusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Join the path
|
||||
//Copy from the go reverse proxy
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
}
|
Loading…
Reference in New Issue
Block a user