mirror of
https://github.com/goharbor/harbor
synced 2025-04-08 04:20:57 +00:00
Fix the multipart form data content missing issue by refill the content again before passing to the backend server
refill the multipart form data at harbor API layer export write error functions from the chartserver package refactor the error format to be compatiable with push plugin
This commit is contained in:
parent
405e89158a
commit
9f0f959749
|
@ -16,6 +16,7 @@ import (
|
|||
|
||||
const (
|
||||
readmeFileName = "README.md"
|
||||
valuesFileName = "values.yaml"
|
||||
)
|
||||
|
||||
//ChartVersionDetails keeps the detailed data info of the chart version
|
||||
|
@ -72,7 +73,7 @@ func (cho *ChartOperator) GetChartDetails(content []byte) (*ChartVersionDetails,
|
|||
values = parseRawValues([]byte(chartData.Values.GetRaw()))
|
||||
if len(values) > 0 {
|
||||
//Append values.yaml file
|
||||
files["values.yaml"] = chartData.Values.Raw
|
||||
files[valuesFileName] = chartData.Values.Raw
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,10 @@ func (cc *ChartClient) GetContent(addr string) ([]byte, error) {
|
|||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
if err := extractError(content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to retrieve content from '%s' with error: %s", fullURI.Path, content)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,19 +49,19 @@ func (mh *ManipulationHandler) ListCharts(w http.ResponseWriter, req *http.Reque
|
|||
|
||||
content, err := mh.apiClient.GetContent(url)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
chartList, err := mh.chartOperator.GetChartList(content)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(chartList)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ func (mh *ManipulationHandler) GetChart(w http.ResponseWriter, req *http.Request
|
|||
func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.Request) {
|
||||
chartV, err := mh.getChartVersion(req.URL.String())
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -97,20 +97,20 @@ func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.
|
|||
}
|
||||
|
||||
if len(strings.TrimSpace(namespace)) == 0 {
|
||||
writeInternalError(w, errors.New("failed to extract namespace from the request"))
|
||||
WriteInternalError(w, errors.New("failed to extract namespace from the request"))
|
||||
return
|
||||
}
|
||||
|
||||
content, err := mh.getChartVersionContent(namespace, chartV.URLs[0])
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
//Process bytes and get more details of chart version
|
||||
chartDetails, err = mh.chartOperator.GetChartDetails(content)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
chartDetails.Metadata = chartV
|
||||
|
@ -127,7 +127,7 @@ func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.
|
|||
|
||||
bytes, err := json.Marshal(chartDetails)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -60,14 +60,14 @@ func (rh *RepositoryHandler) GetIndexFile(w http.ResponseWriter, req *http.Reque
|
|||
//Get project manager references
|
||||
projectMgr, err := filter.GetProjectManager(req)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
//Get all the projects
|
||||
results, err := projectMgr.List(nil)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ LOOP:
|
|||
//All the threads are done
|
||||
//Met an error
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ LOOP:
|
|||
|
||||
bytes, err := yaml.Marshal(mergedIndexFile)
|
||||
if err != nil {
|
||||
writeInternalError(w, err)
|
||||
WriteInternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ const (
|
|||
contentTypeJSON = "application/json"
|
||||
)
|
||||
|
||||
//Write error to http client
|
||||
func writeError(w http.ResponseWriter, code int, err error) {
|
||||
//WriteError writes error to http client
|
||||
func WriteError(w http.ResponseWriter, code int, err error) {
|
||||
errorObj := make(map[string]string)
|
||||
errorObj["error"] = err.Error()
|
||||
errorContent, _ := json.Marshal(errorObj)
|
||||
|
@ -24,9 +24,9 @@ func writeError(w http.ResponseWriter, code int, err error) {
|
|||
w.Write(errorContent)
|
||||
}
|
||||
|
||||
//StatusCode == 500
|
||||
func writeInternalError(w http.ResponseWriter, err error) {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
//WriteInternalError writes error with statusCode == 500
|
||||
func WriteInternalError(w http.ResponseWriter, err error) {
|
||||
WriteError(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
//Write JSON data to http client
|
||||
|
@ -36,6 +36,26 @@ func writeJSONData(w http.ResponseWriter, data []byte) {
|
|||
w.Write(data)
|
||||
}
|
||||
|
||||
//Extract error object '{"error": "****---***"}' from the content if existing
|
||||
//nil error will be returned if it does exist
|
||||
func extractError(content []byte) error {
|
||||
if len(content) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
errorObj := make(map[string]string)
|
||||
err := json.Unmarshal(content, &errorObj)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if errText, ok := errorObj["error"]; ok {
|
||||
return errors.New(errText)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Parse the redis configuration to the beego cache pattern
|
||||
//Config pattern is "address:port[,weight,password,db_index]"
|
||||
func parseRedisConfig(redisConfigV string) (string, error) {
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -25,6 +29,10 @@ const (
|
|||
accessLevelWrite
|
||||
accessLevelAll
|
||||
accessLevelSystem
|
||||
|
||||
formFieldNameForChart = "chart"
|
||||
formFiledNameForProv = "prov"
|
||||
headerContentType = "Content-Type"
|
||||
)
|
||||
|
||||
//chartController is a singleton instance
|
||||
|
@ -162,6 +170,21 @@ func (cra *ChartRepositoryAPI) UploadChartVersion() {
|
|||
return
|
||||
}
|
||||
|
||||
//Rewrite file content
|
||||
formFiles := make([]formFile, 0)
|
||||
formFiles = append(formFiles,
|
||||
formFile{
|
||||
formField: formFieldNameForChart,
|
||||
mustHave: true,
|
||||
},
|
||||
formFile{
|
||||
formField: formFiledNameForProv,
|
||||
})
|
||||
if err := cra.rewriteFileContent(formFiles, cra.Ctx.Request); err != nil {
|
||||
chartserver.WriteInternalError(cra.Ctx.ResponseWriter, err)
|
||||
return
|
||||
}
|
||||
|
||||
chartController.GetManipulationHandler().UploadChartVersion(cra.Ctx.ResponseWriter, cra.Ctx.Request)
|
||||
}
|
||||
|
||||
|
@ -172,6 +195,18 @@ func (cra *ChartRepositoryAPI) UploadChartProvFile() {
|
|||
return
|
||||
}
|
||||
|
||||
//Rewrite file content
|
||||
formFiles := make([]formFile, 0)
|
||||
formFiles = append(formFiles,
|
||||
formFile{
|
||||
formField: formFiledNameForProv,
|
||||
mustHave: true,
|
||||
})
|
||||
if err := cra.rewriteFileContent(formFiles, cra.Ctx.Request); err != nil {
|
||||
chartserver.WriteInternalError(cra.Ctx.ResponseWriter, err)
|
||||
return
|
||||
}
|
||||
|
||||
chartController.GetManipulationHandler().UploadProvenanceFile(cra.Ctx.ResponseWriter, cra.Ctx.Request)
|
||||
}
|
||||
|
||||
|
@ -310,3 +345,63 @@ func initializeChartController() (*chartserver.Controller, error) {
|
|||
|
||||
return controller, nil
|
||||
}
|
||||
|
||||
//formFile is used to represent the uploaded files in the form
|
||||
type formFile struct {
|
||||
//form field key contains the form file
|
||||
formField string
|
||||
|
||||
//flag to indicate if the file identified by the 'formField'
|
||||
//must exist
|
||||
mustHave bool
|
||||
}
|
||||
|
||||
//If the files are uploaded with multipart/form-data mimetype, beego will extract the data
|
||||
//from the request automatically. Then the request passed to the backend server with proxying
|
||||
//way will have empty content.
|
||||
//This method will refill the requests with file content.
|
||||
func (cra *ChartRepositoryAPI) rewriteFileContent(files []formFile, request *http.Request) error {
|
||||
if len(files) == 0 {
|
||||
return nil //no files, early return
|
||||
}
|
||||
|
||||
var body bytes.Buffer
|
||||
w := multipart.NewWriter(&body)
|
||||
defer func() {
|
||||
if err := w.Close(); err != nil {
|
||||
//Just log it
|
||||
hlog.Errorf("Failed to defer close multipart writer with error: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
//Process files by key one by one
|
||||
for _, f := range files {
|
||||
mFile, mHeader, err := cra.GetFile(f.formField)
|
||||
//Handle error case by case
|
||||
if err != nil {
|
||||
formatedErr := fmt.Errorf("Get file content with multipart header from key '%s' failed with error: %s", f.formField, err.Error())
|
||||
if f.mustHave || err != http.ErrMissingFile {
|
||||
return formatedErr
|
||||
}
|
||||
|
||||
//Error can be ignored, just log it
|
||||
hlog.Warning(formatedErr.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
fw, err := w.CreateFormFile(f.formField, mHeader.Filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Create form file with multipart header failed with error: %s", err.Error())
|
||||
}
|
||||
|
||||
_, err = io.Copy(fw, mFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Copy file stream in multipart form data failed with error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
request.Header.Set(headerContentType, w.FormDataContentType())
|
||||
request.Body = ioutil.NopCloser(&body)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
BIN
src/ui/harbor_ui
Executable file
BIN
src/ui/harbor_ui
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user