mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 02:35:17 +01:00
Implement the related handler methods of the transparent operations based on proxy
support index file support object downloading support health checking support related manipulating operations except getting chart version
This commit is contained in:
parent
54a0d10994
commit
ce4e5ac01d
@ -2,15 +2,16 @@ package chartserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//BaseHandler defines the handlers related with the chart server itself.
|
//BaseHandler defines the handlers related with the chart server itself.
|
||||||
type BaseHandler struct {
|
type BaseHandler struct {
|
||||||
//Proxy used to to transfer the traffic of requests
|
//Proxy used to to transfer the traffic of requests
|
||||||
//It's mainly used to talk to the backend chart server
|
//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
|
//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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
"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")
|
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
|
//Use customized reverse proxy
|
||||||
proxy := httputil.NewSingleHostReverseProxy(backendServer)
|
proxy := NewProxyEngine(backendServer)
|
||||||
|
|
||||||
//Initialize chart operator for use
|
//Initialize chart operator for use
|
||||||
operator := &ChartOperator{}
|
operator := &ChartOperator{}
|
||||||
|
@ -2,7 +2,6 @@ package chartserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//ManipulationHandler includes all the handler methods for the purpose of manipulating the
|
//ManipulationHandler includes all the handler methods for the purpose of manipulating the
|
||||||
@ -10,28 +9,41 @@ import (
|
|||||||
type ManipulationHandler struct {
|
type ManipulationHandler struct {
|
||||||
//Proxy used to to transfer the traffic of requests
|
//Proxy used to to transfer the traffic of requests
|
||||||
//It's mainly used to talk to the backend chart server
|
//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
|
//Parse and process the chart version to provide required info data
|
||||||
chartOperator *ChartOperator
|
chartOperator *ChartOperator
|
||||||
}
|
}
|
||||||
|
|
||||||
//ListCharts lists all the charts under the specified namespace
|
//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
|
//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
|
//GetChartVersion get the specified version for one chart
|
||||||
//This handler should return the details of the chart version,
|
//This handler should return the details of the chart version,
|
||||||
//maybe including metadata,dependencies and values etc.
|
//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
|
//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
|
//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
|
//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 (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//RepositoryHandler defines all the handlers to handle the requests related with chart repository
|
//RepositoryHandler defines all the handlers to handle the requests related with chart repository
|
||||||
@ -10,18 +9,24 @@ import (
|
|||||||
type RepositoryHandler struct {
|
type RepositoryHandler struct {
|
||||||
//Proxy used to to transfer the traffic of requests
|
//Proxy used to to transfer the traffic of requests
|
||||||
//It's mainly used to talk to the backend chart server
|
//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
|
//GetIndexFileWithNS will read the index.yaml data under the specified namespace
|
||||||
func (rh *RepositoryHandler) GetIndexFileWithNS(w http.ResponseWriter, req *http.Request) {
|
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
|
//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
|
//Please be aware that, to support this function, the backend chart repository server should
|
||||||
//enable multi-tenancies
|
//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
|
//DownloadChartObject will download the stored chart object to the client
|
||||||
//e.g: helm install
|
//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