mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 22:57:38 +01:00
Merge pull request #10516 from wy65701436/middleware-mf
add get manifest information middleware in new v2 hanlder
This commit is contained in:
commit
2fa9d0ce45
74
src/server/middleware/manifestinfo/manifest_info.go
Normal file
74
src/server/middleware/manifestinfo/manifest_info.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package manifestinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
|
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||||
|
project2 "github.com/goharbor/harbor/src/pkg/project"
|
||||||
|
"github.com/goharbor/harbor/src/server/middleware"
|
||||||
|
reg_err "github.com/goharbor/harbor/src/server/registry/error"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
manifestURLRe = regexp.MustCompile(`^/v2/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)manifests/([\w][\w.:-]{0,127})`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Middleware gets the manifest information from request and inject it into the context
|
||||||
|
func Middleware() func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
mf, err := parseManifestInfoFromPath(req)
|
||||||
|
if err != nil {
|
||||||
|
reg_err.Handle(rw, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*req = *(req.WithContext(middleware.NewManifestInfoContext(req.Context(), mf)))
|
||||||
|
next.ServeHTTP(rw, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseManifestInfoFromPath parse manifest from request path
|
||||||
|
func parseManifestInfoFromPath(req *http.Request) (*middleware.ManifestInfo, error) {
|
||||||
|
match, repository, reference := MatchManifestURL(req)
|
||||||
|
if !match {
|
||||||
|
return nil, fmt.Errorf("not match url %s for manifest", req.URL.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
projectName, _ := utils.ParseRepository(repository)
|
||||||
|
project, err := project2.Mgr.Get(projectName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get project %s, error: %v", projectName, err)
|
||||||
|
}
|
||||||
|
if project == nil {
|
||||||
|
return nil, ierror.NotFoundError(nil).WithMessage("project %s not found", projectName)
|
||||||
|
}
|
||||||
|
|
||||||
|
info := &middleware.ManifestInfo{
|
||||||
|
ProjectID: project.ProjectID,
|
||||||
|
Repository: repository,
|
||||||
|
}
|
||||||
|
|
||||||
|
dgt, err := digest.Parse(reference)
|
||||||
|
if err != nil {
|
||||||
|
info.Tag = reference
|
||||||
|
} else {
|
||||||
|
info.Digest = dgt.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchManifestURL ...
|
||||||
|
func MatchManifestURL(req *http.Request) (bool, string, string) {
|
||||||
|
s := manifestURLRe.FindStringSubmatch(req.URL.Path)
|
||||||
|
if len(s) == 3 {
|
||||||
|
s[1] = strings.TrimSuffix(s[1], "/")
|
||||||
|
return true, s[1], s[2]
|
||||||
|
}
|
||||||
|
return false, "", ""
|
||||||
|
}
|
104
src/server/middleware/manifestinfo/manifest_info_test.go
Normal file
104
src/server/middleware/manifestinfo/manifest_info_test.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package manifestinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
|
"github.com/goharbor/harbor/src/server/middleware"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mfinfoTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
require *require.Assertions
|
||||||
|
assert *assert.Assertions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mfinfoTestSuite) SetupSuite() {
|
||||||
|
t.require = require.New(t.T())
|
||||||
|
t.assert = assert.New(t.T())
|
||||||
|
test.InitDatabaseFromEnv()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mfinfoTestSuite) TestParseManifestInfoFromPath() {
|
||||||
|
mustRequest := func(method, url string) *http.Request {
|
||||||
|
req, _ := http.NewRequest(method, url, nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
req *http.Request
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *middleware.ManifestInfo
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"ok for digest",
|
||||||
|
args{mustRequest(http.MethodDelete, "/v2/library/photon/manifests/sha256:3e17b60ab9d92d953fb8ebefa25624c0d23fb95f78dde5572285d10158044059")},
|
||||||
|
&middleware.ManifestInfo{
|
||||||
|
ProjectID: 1,
|
||||||
|
Repository: "library/photon",
|
||||||
|
Digest: "sha256:3e17b60ab9d92d953fb8ebefa25624c0d23fb95f78dde5572285d10158044059",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ok for tag",
|
||||||
|
args{mustRequest(http.MethodDelete, "/v2/library/photon/manifests/latest")},
|
||||||
|
&middleware.ManifestInfo{
|
||||||
|
ProjectID: 1,
|
||||||
|
Repository: "library/photon",
|
||||||
|
Tag: "latest",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"project not found",
|
||||||
|
args{mustRequest(http.MethodDelete, "/v2/notfound/photon/manifests/latest")},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url not match",
|
||||||
|
args{mustRequest(http.MethodDelete, "/v2/library/photon/manifest/latest")},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func() {
|
||||||
|
got, err := parseManifestInfoFromPath(tt.args.req)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf(err, fmt.Sprintf("ParseManifestInfoFromPath() error = %v, wantErr %v", err, tt.wantErr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf(err, fmt.Sprintf("ParseManifestInfoFromPath() = %v, want %v", got, tt.want))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mfinfoTestSuite) TestResolveManifest() {
|
||||||
|
|
||||||
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodDelete, "/v2/library/hello-world/manifests/latest", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
Middleware()(next).ServeHTTP(rec, req)
|
||||||
|
t.assert.Equal(rec.Code, http.StatusOK)
|
||||||
|
|
||||||
|
mf, ok := middleware.ManifestInfoFromContext(req.Context())
|
||||||
|
t.assert.True(ok)
|
||||||
|
t.assert.Equal(mf.Tag, "latest")
|
||||||
|
t.assert.Equal(mf.ProjectID, int64(1))
|
||||||
|
}
|
31
src/server/middleware/util.go
Normal file
31
src/server/middleware/util.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// manifestInfoKey the context key for manifest info
|
||||||
|
manifestInfoKey = contextKey("ManifestInfo")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ManifestInfo ...
|
||||||
|
type ManifestInfo struct {
|
||||||
|
ProjectID int64
|
||||||
|
Repository string
|
||||||
|
Tag string
|
||||||
|
Digest string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewManifestInfoContext returns context with manifest info
|
||||||
|
func NewManifestInfoContext(ctx context.Context, info *ManifestInfo) context.Context {
|
||||||
|
return context.WithValue(ctx, manifestInfoKey, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ManifestInfoFromContext returns manifest info from context
|
||||||
|
func ManifestInfoFromContext(ctx context.Context) (*ManifestInfo, bool) {
|
||||||
|
info, ok := ctx.Value(manifestInfoKey).(*ManifestInfo)
|
||||||
|
return info, ok
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user