Add event trigger to helm upload/deletion replication

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2019-04-15 10:44:56 +08:00
parent f0fa90d10f
commit 7a373c2eed
7 changed files with 105 additions and 5 deletions

View File

@ -7,7 +7,13 @@ import (
"strings"
"github.com/ghodss/yaml"
"github.com/goharbor/harbor/src/replication"
rep_event "github.com/goharbor/harbor/src/replication/event"
"github.com/goharbor/harbor/src/replication/model"
helm_repo "k8s.io/helm/pkg/repo"
"github.com/goharbor/harbor/src/common/utils/log"
"os"
)
// ListCharts gets the chart list under the namespace
@ -63,7 +69,38 @@ func (c *Controller) DeleteChartVersion(namespace, chartName, version string) er
url := fmt.Sprintf("%s/%s/%s", c.APIPrefix(namespace), chartName, version)
return c.apiClient.DeleteContent(url)
err := c.apiClient.DeleteContent(url)
if err != nil {
return err
}
// send notification to replication handler
// Todo: it used as the replacement of webhook, will be removed when webhook to be introduced.
if os.Getenv("UTTEST") != "true" {
go func() {
e := &rep_event.Event{
Type: rep_event.EventTypeChartDelete,
Resource: &model.Resource{
Type: model.ResourceTypeChart,
Deleted: true,
Metadata: &model.ResourceMetadata{
Namespace: &model.Namespace{
Name: namespace,
},
Repository: &model.Repository{
Name: chartName,
},
Vtags: []string{version},
},
},
}
if err := replication.EventHandler.Handle(e); err != nil {
log.Errorf("failed to handle event: %v", err)
}
}()
}
return nil
}
// GetChartVersion returns the summary of the specified chart version.

View File

@ -4,6 +4,11 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/common"
hlog "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/replication"
rep_event "github.com/goharbor/harbor/src/replication/event"
"github.com/goharbor/harbor/src/replication/model"
"io/ioutil"
"log"
"net/http"
@ -80,6 +85,39 @@ func director(target *url.URL, cred *Credential, req *http.Request) {
// Modify the http response
func modifyResponse(res *http.Response) error {
// Upload chart success, then to the notification to replication handler
if res.StatusCode == http.StatusCreated {
// 201 and has chart_upload(namespace-repository-version) context
// means this response is for uploading chart success.
chartUpload := res.Request.Context().Value(common.ChartUploadCtxKey).(string)
if chartUpload != "" {
chartUploadSplitted := strings.Split(chartUpload, ":")
if len(chartUploadSplitted) == 3 {
// Todo: it used as the replacement of webhook, will be removed when webhook to be introduced.
go func() {
e := &rep_event.Event{
Type: rep_event.EventTypeChartUpload,
Resource: &model.Resource{
Type: model.ResourceTypeChart,
Metadata: &model.ResourceMetadata{
Namespace: &model.Namespace{
Name: chartUploadSplitted[0],
},
Repository: &model.Repository{
Name: chartUploadSplitted[1],
},
Vtags: []string{chartUploadSplitted[2]},
},
},
}
if err := replication.EventHandler.Handle(e); err != nil {
hlog.Errorf("failed to handle event: %v", err)
}
}()
}
}
}
// Accept cases
// Success or redirect
if res.StatusCode >= http.StatusOK && res.StatusCode <= http.StatusTemporaryRedirect {

View File

@ -14,6 +14,8 @@
package common
type contextKey string
// const variables
const (
DBAuth = "db_auth"
@ -136,4 +138,6 @@ const (
RobotTokenDuration = "robot_token_duration"
OIDCCallbackPath = "/c/oidc/callback"
ChartUploadCtxKey = contextKey("chart_upload")
)

View File

@ -2,6 +2,7 @@ package api
import (
"bytes"
"context"
"errors"
"fmt"
"io"
@ -297,8 +298,28 @@ func (cra *ChartRepositoryAPI) UploadChartVersion() {
}
}
// 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, cra.Ctx.Request)
chartController.ProxyTraffic(cra.Ctx.ResponseWriter, req)
}
// UploadChartProvFile handles POST /api/:repo/prov

View File

@ -108,7 +108,7 @@ func (c *controller) createFlow(executionID int64, policy *model.Policy, resourc
{
Type: model.FilterTypeName,
// TODO only filter the repo part?
Value: resource.Metadata.GetResourceName(),
Value: resource.Metadata.Repository.Name,
},
{
Type: model.FilterTypeTag,

View File

@ -118,7 +118,7 @@ func filterResources(resources []*model.Resource, filters []*model.Filter) ([]*m
for _, filter := range filters {
switch filter.Type {
case model.FilterTypeResource:
resourceType, ok := filter.Value.(string)
resourceType, ok := filter.Value.(model.ResourceType)
if !ok {
return nil, fmt.Errorf("%v is not a valid string", filter.Value)
}

View File

@ -289,7 +289,7 @@ func TestFilterResources(t *testing.T) {
filters := []*model.Filter{
{
Type: model.FilterTypeResource,
Value: string(model.ResourceTypeChart),
Value: model.ResourceTypeChart,
},
{
Type: model.FilterTypeName,