Merge pull request #5242 from steven-zou/chart_repo_supporting

Implement the related handler methods of the transparent operations
This commit is contained in:
Daniel Jiang 2018-07-04 21:16:07 +08:00 committed by GitHub
commit e1474ac50b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 150 additions and 18 deletions

View File

@ -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)
}

View File

@ -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{}

View File

@ -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)
}

View File

@ -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)
}

View 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
}