mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-12 02:41:50 +01:00
Add event trigger to helm upload/deletion replication
Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
f0fa90d10f
commit
7a373c2eed
@ -7,7 +7,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"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"
|
helm_repo "k8s.io/helm/pkg/repo"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListCharts gets the chart list under the namespace
|
// 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)
|
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.
|
// GetChartVersion returns the summary of the specified chart version.
|
||||||
|
@ -4,6 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"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"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -80,6 +85,39 @@ func director(target *url.URL, cred *Credential, req *http.Request) {
|
|||||||
|
|
||||||
// Modify the http response
|
// Modify the http response
|
||||||
func modifyResponse(res *http.Response) error {
|
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
|
// Accept cases
|
||||||
// Success or redirect
|
// Success or redirect
|
||||||
if res.StatusCode >= http.StatusOK && res.StatusCode <= http.StatusTemporaryRedirect {
|
if res.StatusCode >= http.StatusOK && res.StatusCode <= http.StatusTemporaryRedirect {
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
// const variables
|
// const variables
|
||||||
const (
|
const (
|
||||||
DBAuth = "db_auth"
|
DBAuth = "db_auth"
|
||||||
@ -136,4 +138,6 @@ const (
|
|||||||
RobotTokenDuration = "robot_token_duration"
|
RobotTokenDuration = "robot_token_duration"
|
||||||
|
|
||||||
OIDCCallbackPath = "/c/oidc/callback"
|
OIDCCallbackPath = "/c/oidc/callback"
|
||||||
|
|
||||||
|
ChartUploadCtxKey = contextKey("chart_upload")
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"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
|
// 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
|
// UploadChartProvFile handles POST /api/:repo/prov
|
||||||
|
@ -108,7 +108,7 @@ func (c *controller) createFlow(executionID int64, policy *model.Policy, resourc
|
|||||||
{
|
{
|
||||||
Type: model.FilterTypeName,
|
Type: model.FilterTypeName,
|
||||||
// TODO only filter the repo part?
|
// TODO only filter the repo part?
|
||||||
Value: resource.Metadata.GetResourceName(),
|
Value: resource.Metadata.Repository.Name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: model.FilterTypeTag,
|
Type: model.FilterTypeTag,
|
||||||
|
@ -118,7 +118,7 @@ func filterResources(resources []*model.Resource, filters []*model.Filter) ([]*m
|
|||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
switch filter.Type {
|
switch filter.Type {
|
||||||
case model.FilterTypeResource:
|
case model.FilterTypeResource:
|
||||||
resourceType, ok := filter.Value.(string)
|
resourceType, ok := filter.Value.(model.ResourceType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("%v is not a valid string", filter.Value)
|
return nil, fmt.Errorf("%v is not a valid string", filter.Value)
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,7 @@ func TestFilterResources(t *testing.T) {
|
|||||||
filters := []*model.Filter{
|
filters := []*model.Filter{
|
||||||
{
|
{
|
||||||
Type: model.FilterTypeResource,
|
Type: model.FilterTypeResource,
|
||||||
Value: string(model.ResourceTypeChart),
|
Value: model.ResourceTypeChart,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: model.FilterTypeName,
|
Type: model.FilterTypeName,
|
||||||
|
Loading…
Reference in New Issue
Block a user