Fix chart upload issue on event based

Use chart API to load the uploaded chart file to get the name and version

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2019-04-19 16:57:54 +08:00
parent ac4dfd5973
commit df6e0600c9
4 changed files with 63 additions and 30 deletions

View File

@ -14,6 +14,7 @@ import (
"github.com/goharbor/harbor/src/common/models"
hlog "github.com/goharbor/harbor/src/common/utils/log"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
helm_repo "k8s.io/helm/pkg/repo"
)
@ -71,13 +72,7 @@ type ChartOperator struct{}
// GetChartDetails parse the details from the provided content bytes
func (cho *ChartOperator) GetChartDetails(content []byte) (*ChartVersionDetails, error) {
if content == nil || len(content) == 0 {
return nil, errors.New("zero content")
}
// Load chart from in-memory content
reader := bytes.NewReader(content)
chartData, err := chartutil.LoadArchive(reader)
chartData, err := cho.GetChartData(content)
if err != nil {
return nil, err
}
@ -164,6 +159,21 @@ func (cho *ChartOperator) GetChartList(content []byte) ([]*ChartInfo, error) {
return chartList, nil
}
// GetChartData returns raw data of chart
func (cho *ChartOperator) GetChartData(content []byte) (*chart.Chart, error) {
if content == nil || len(content) == 0 {
return nil, errors.New("zero content")
}
reader := bytes.NewReader(content)
chartData, err := chartutil.LoadArchive(reader)
if err != nil {
return nil, err
}
return chartData, nil
}
// GetChartVersions returns the chart versions
func (cho *ChartOperator) GetChartVersions(content []byte) (ChartVersions, error) {
if content == nil || len(content) == 0 {

View File

@ -92,7 +92,7 @@ func modifyResponse(res *http.Response) error {
// means this response is for uploading chart success.
chartUpload := res.Request.Context().Value(common.ChartUploadCtxKey).(string)
if chartUpload != "" {
chartUploadSplitted := strings.Split(chartUpload, ":")
chartUploadSplitted := strings.Split(chartUpload, common.ChartUploadCtxSeparator)
if len(chartUploadSplitted) == 3 {
// Todo: it used as the replacement of webhook, will be removed when webhook to be introduced.
go func() {

View File

@ -139,5 +139,6 @@ const (
OIDCCallbackPath = "/c/oidc/callback"
ChartUploadCtxKey = contextKey("chart_upload")
ChartUploadCtxKey = contextKey("chart_upload")
ChartUploadCtxSeparator = "^{chartUP}"
)

View File

@ -296,30 +296,13 @@ func (cra *ChartRepositoryAPI) UploadChartVersion() {
cra.SendInternalServerError(err)
return
}
if err := cra.addEventContext(formFiles, cra.Ctx.Request); err != nil {
hlog.Infof("Failed to add chart upload context into request, which could lead to no execution of event based chart replication policy, %v", err)
}
}
// set namespace/repository/version for replication event.
_, header, err := cra.GetFile(formFieldNameForChart)
if err != nil {
cra.SendInternalServerError(err)
return
}
req := cra.Ctx.Request
charFileName := header.Filename
if !strings.HasSuffix(charFileName, ".tgz") {
cra.SendInternalServerError(fmt.Errorf("chart file expected %s to end with .tgz", charFileName))
return
}
charFileName = strings.TrimSuffix(charFileName, ".tgz")
// colon cannot be used as namespace
charFileName = strings.Replace(charFileName, "-", ":", -1)
// value sample: library:redis:4.0.3 (namespace:repository:version)
ctx := context.WithValue(cra.Ctx.Request.Context(), common.ChartUploadCtxKey, cra.namespace+":"+charFileName)
req = req.WithContext(ctx)
// Directly proxy to the backend
chartController.ProxyTraffic(cra.Ctx.ResponseWriter, req)
chartController.ProxyTraffic(cra.Ctx.ResponseWriter, cra.Ctx.Request)
}
// UploadChartProvFile handles POST /api/:repo/prov
@ -428,6 +411,43 @@ type formFile struct {
mustHave bool
}
// The func is for event based chart replication policy.
// It will add a context for uploading request with key chart_upload, and consumed by upload response.
// Context Sample: library^{chartUP}harbor^{chartUP}0.2.0
func (cra *ChartRepositoryAPI) addEventContext(files []formFile, request *http.Request) error {
if len(files) == 0 {
return nil
}
for _, f := range files {
if f.formField == formFieldNameForChart {
mFile, _, err := cra.GetFile(f.formField)
if err != nil {
hlog.Infof("failed to read file content for upload event, %v", err)
return err
}
var Buf bytes.Buffer
_, err = io.Copy(&Buf, mFile)
if err != nil {
hlog.Infof("failed to read file content for upload event, %v", err)
return err
}
chartOpr := chartserver.ChartOperator{}
chartDetails, err := chartOpr.GetChartData(Buf.Bytes())
if err != nil {
hlog.Infof("failed to get chart content for upload event, %v", err)
return err
}
*request = *(request.WithContext(context.WithValue(request.Context(), common.ChartUploadCtxKey, cra.namespace+
common.ChartUploadCtxSeparator+chartDetails.Metadata.Name+
common.ChartUploadCtxSeparator+chartDetails.Metadata.Version)))
break
}
}
return nil
}
// 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.
@ -449,6 +469,7 @@ func (cra *ChartRepositoryAPI) rewriteFileContent(files []formFile, request *htt
// 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())
@ -470,6 +491,7 @@ func (cra *ChartRepositoryAPI) rewriteFileContent(files []formFile, request *htt
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())