From df6e0600c97d22c6a6a2e35e29a7524c9b3fa356 Mon Sep 17 00:00:00 2001 From: wang yan Date: Fri, 19 Apr 2019 16:57:54 +0800 Subject: [PATCH] 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 --- src/chartserver/chart_operator.go | 24 ++++++++---- src/chartserver/reverse_proxy.go | 2 +- src/common/const.go | 3 +- src/core/api/chart_repository.go | 64 +++++++++++++++++++++---------- 4 files changed, 63 insertions(+), 30 deletions(-) diff --git a/src/chartserver/chart_operator.go b/src/chartserver/chart_operator.go index a0f83a5d6..daf4b8753 100644 --- a/src/chartserver/chart_operator.go +++ b/src/chartserver/chart_operator.go @@ -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 { diff --git a/src/chartserver/reverse_proxy.go b/src/chartserver/reverse_proxy.go index 058eef566..e4c02d7cf 100644 --- a/src/chartserver/reverse_proxy.go +++ b/src/chartserver/reverse_proxy.go @@ -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() { diff --git a/src/common/const.go b/src/common/const.go index 9a6ad2560..ab5d3ac18 100644 --- a/src/common/const.go +++ b/src/common/const.go @@ -139,5 +139,6 @@ const ( OIDCCallbackPath = "/c/oidc/callback" - ChartUploadCtxKey = contextKey("chart_upload") + ChartUploadCtxKey = contextKey("chart_upload") + ChartUploadCtxSeparator = "^{chartUP}" ) diff --git a/src/core/api/chart_repository.go b/src/core/api/chart_repository.go index c7370ea85..441d47f94 100644 --- a/src/core/api/chart_repository.go +++ b/src/core/api/chart_repository.go @@ -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())