Move ArtifactInfo to internal package (#11055)

To avoid depedency loop, this commit moves the model of ArtifactInfo to
internal pacakge, so that a controller can it from context when needed.

Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
Daniel Jiang 2020-03-13 11:16:13 +08:00 committed by GitHub
parent 2a243ef7a2
commit 2e7eb8872e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 107 additions and 93 deletions

View File

@ -113,7 +113,7 @@ func (c *controllerTestSuite) TestAssembleArtifact() {
},
}
c.tagCtl.On("List").Return([]*tag.Tag{tg}, nil)
ctx := internal.SetAPIVersion(nil, "2.0")
ctx := internal.WithAPIVersion(nil, "2.0")
lb := &models.Label{
ID: 1,
Name: "label",

View File

@ -20,9 +20,22 @@ type contextKey string
// define all context key here to avoid conflict
const (
contextKeyAPIVersion contextKey = "apiVersion"
contextKeyAPIVersion contextKey = "apiVersion"
contextKeyArtifactInfo contextKey = "artifactInfo"
)
// ArtifactInfo wraps the artifact info extracted from the request to "/v2/"
type ArtifactInfo struct {
Repository string
Reference string
ProjectName string
Digest string
Tag string
BlobMountRepository string
BlobMountProjectName string
BlobMountDigest string
}
func setToContext(ctx context.Context, key contextKey, value interface{}) context.Context {
if ctx == nil {
ctx = context.Background()
@ -37,8 +50,8 @@ func getFromContext(ctx context.Context, key contextKey) interface{} {
return ctx.Value(key)
}
// SetAPIVersion sets the API version into the context
func SetAPIVersion(ctx context.Context, version string) context.Context {
// WithAPIVersion returns a context with APIVersion set
func WithAPIVersion(ctx context.Context, version string) context.Context {
return setToContext(ctx, contextKeyAPIVersion, version)
}
@ -51,3 +64,17 @@ func GetAPIVersion(ctx context.Context) string {
}
return version
}
// WithArtifactInfo returns a context with ArtifactInfo set
func WithArtifactInfo(ctx context.Context, art ArtifactInfo) context.Context {
return setToContext(ctx, contextKeyArtifactInfo, art)
}
// GetArtifactInfo gets the ArtifactInfo from the context
func GetArtifactInfo(ctx context.Context) (art ArtifactInfo) {
value := getFromContext(ctx, contextKeyArtifactInfo)
if value != nil {
art = value.(ArtifactInfo)
}
return
}

View File

@ -21,7 +21,7 @@ import (
)
func TestSetAPIVersion(t *testing.T) {
ctx := SetAPIVersion(context.Background(), "1.0")
ctx := WithAPIVersion(context.Background(), "1.0")
assert.NotNil(t, ctx)
}
@ -35,7 +35,7 @@ func TestGetAPIVersion(t *testing.T) {
assert.Empty(t, version)
// version set in context
ctx := SetAPIVersion(context.Background(), "1.0")
ctx := WithAPIVersion(context.Background(), "1.0")
version = GetAPIVersion(ctx)
assert.Equal(t, "1.0", version)
}

View File

@ -24,7 +24,7 @@ import (
func Middleware(version string) middleware.Middleware {
return func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := internal.SetAPIVersion(req.Context(), version)
ctx := internal.WithAPIVersion(req.Context(), version)
handler.ServeHTTP(w, req.WithContext(ctx))
})
}

View File

@ -15,17 +15,18 @@
package artifactinfo
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/common/utils/log"
ierror "github.com/goharbor/harbor/src/internal/error"
serror "github.com/goharbor/harbor/src/server/error"
"github.com/goharbor/harbor/src/server/middleware"
"github.com/opencontainers/go-digest"
"net/http"
"net/url"
"regexp"
"strings"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/internal"
ierror "github.com/goharbor/harbor/src/internal/error"
serror "github.com/goharbor/harbor/src/server/error"
"github.com/goharbor/harbor/src/server/middleware"
"github.com/opencontainers/go-digest"
)
const (
@ -61,7 +62,7 @@ func Middleware() func(http.Handler) http.Handler {
serror.SendError(rw, ierror.BadRequestError(err))
return
}
art := &middleware.ArtifactInfo{
art := internal.ArtifactInfo{
Repository: repo,
ProjectName: pn,
}
@ -86,7 +87,7 @@ func Middleware() func(http.Handler) http.Handler {
art.BlobMountProjectName = bmp
art.BlobMountRepository = bmr
}
ctx := context.WithValue(req.Context(), middleware.ArtifactInfoKey, art)
ctx := internal.WithArtifactInfo(req.Context(), art)
next.ServeHTTP(rw, req.WithContext(ctx))
})
}

View File

@ -16,12 +16,14 @@ package artifactinfo
import (
"context"
"github.com/goharbor/harbor/src/server/middleware"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/goharbor/harbor/src/internal"
"github.com/goharbor/harbor/src/server/middleware"
"github.com/stretchr/testify/assert"
)
func TestParseURL(t *testing.T) {
@ -121,20 +123,21 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
func TestPopulateArtifactInfo(t *testing.T) {
none := internal.ArtifactInfo{}
cases := []struct {
req *http.Request
sc int
art *middleware.ArtifactInfo
art internal.ArtifactInfo
}{
{
req: httptest.NewRequest(http.MethodDelete, "/v2/hello-world/manifests/latest", nil),
sc: http.StatusBadRequest,
art: nil,
art: none,
},
{
req: httptest.NewRequest(http.MethodDelete, "/v2/library/hello-world/manifests/latest", nil),
sc: http.StatusOK,
art: &middleware.ArtifactInfo{
art: internal.ArtifactInfo{
Repository: "library/hello-world",
Reference: "latest",
ProjectName: "library",
@ -144,12 +147,12 @@ func TestPopulateArtifactInfo(t *testing.T) {
{
req: httptest.NewRequest(http.MethodPost, "/v2/library/ubuntu/blobs/uploads/?mount=sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f&from=no-project", nil),
sc: http.StatusBadRequest,
art: nil,
art: none,
},
{
req: httptest.NewRequest(http.MethodPost, "/v2/library/ubuntu/blobs/uploads/?from=old/ubuntu&mount=sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f", nil),
sc: http.StatusOK,
art: &middleware.ArtifactInfo{
art: internal.ArtifactInfo{
Repository: "library/ubuntu",
ProjectName: "library",
BlobMountRepository: "old/ubuntu",
@ -160,7 +163,7 @@ func TestPopulateArtifactInfo(t *testing.T) {
{
req: httptest.NewRequest(http.MethodDelete, "/v2/library/hello-world/manifests/sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f", nil),
sc: http.StatusOK,
art: &middleware.ArtifactInfo{
art: internal.ArtifactInfo{
Repository: "library/hello-world",
Reference: "sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f",
Digest: "sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f",
@ -175,10 +178,10 @@ func TestPopulateArtifactInfo(t *testing.T) {
Middleware()(next).ServeHTTP(rec, tt.req)
assert.Equal(t, tt.sc, rec.Code)
if tt.art != nil {
a, ok := middleware.ArtifactInfoFromContext(next.ctx)
assert.True(t, ok)
assert.Equal(t, *tt.art, a)
if tt.art != none {
a := internal.GetArtifactInfo(next.ctx)
assert.NotEqual(t, none, a)
assert.Equal(t, tt.art, a)
}
}
}

View File

@ -7,6 +7,7 @@ import (
"github.com/goharbor/harbor/src/api/project"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/internal"
internal_errors "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/signature"
serror "github.com/goharbor/harbor/src/server/error"
@ -44,13 +45,13 @@ func Middleware() func(http.Handler) http.Handler {
}
}
func validate(req *http.Request) (bool, middleware.ArtifactInfo) {
none := middleware.ArtifactInfo{}
func validate(req *http.Request) (bool, internal.ArtifactInfo) {
none := internal.ArtifactInfo{}
if err := middleware.EnsureArtifactDigest(req.Context()); err != nil {
return false, none
}
af, ok := middleware.ArtifactInfoFromContext(req.Context())
if !ok {
af := internal.GetArtifactInfo(req.Context())
if af == none {
return false, none
}
pro, err := project.Ctl.GetByName(req.Context(), af.ProjectName)
@ -73,7 +74,7 @@ func validate(req *http.Request) (bool, middleware.ArtifactInfo) {
// isArtifactSigned use the sign manager to check the signature, it could handle pull by tag or digtest
// if pull by digest, any tag of the artifact is signed, will return true.
func isArtifactSigned(req *http.Request, art middleware.ArtifactInfo) (bool, error) {
func isArtifactSigned(req *http.Request, art internal.ArtifactInfo) (bool, error) {
checker, err := signature.GetManager().GetCheckerByRepo(req.Context(), art.Repository)
if err != nil {
return false, err

View File

@ -3,14 +3,15 @@ package immutable
import (
"errors"
"fmt"
"net/http"
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/api/tag"
common_util "github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/internal"
internal_errors "github.com/goharbor/harbor/src/internal/error"
serror "github.com/goharbor/harbor/src/server/error"
"github.com/goharbor/harbor/src/server/middleware"
"net/http"
)
// Middleware ...
@ -37,8 +38,9 @@ func Middleware() func(http.Handler) http.Handler {
// If the pushing image matched by any of immutable rule, will have to whether it is the first time to push it,
// as the immutable rule only impacts the existing tag.
func handlePush(req *http.Request) error {
art, ok := middleware.ArtifactInfoFromContext(req.Context())
if !ok {
none := internal.ArtifactInfo{}
art := internal.GetArtifactInfo(req.Context())
if art == none {
return errors.New("cannot get the manifest information from request context")
}

View File

@ -12,6 +12,7 @@ import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/internal"
internal_orm "github.com/goharbor/harbor/src/internal/orm"
"github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/immutabletag"
@ -19,7 +20,6 @@ import (
"github.com/goharbor/harbor/src/pkg/repository"
"github.com/goharbor/harbor/src/pkg/tag"
tag_model "github.com/goharbor/harbor/src/pkg/tag/model/tag"
"github.com/goharbor/harbor/src/server/middleware"
"github.com/opencontainers/go-digest"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
@ -35,7 +35,7 @@ func doPutManifestRequest(projectID int64, projectName, name, tag, dgt string, n
url := fmt.Sprintf("/v2/%s/manifests/%s", repository, tag)
req, _ := http.NewRequest("PUT", url, nil)
afInfo := &middleware.ArtifactInfo{
afInfo := internal.ArtifactInfo{
ProjectName: projectName,
Repository: repository,
Tag: tag,
@ -52,7 +52,7 @@ func doPutManifestRequest(projectID int64, projectName, name, tag, dgt string, n
}
}
*req = *(req.WithContext(internal_orm.NewContext(context.TODO(), dao.GetOrmer())))
*req = *(req.WithContext(context.WithValue(req.Context(), middleware.ArtifactInfoKey, afInfo)))
*req = *(req.WithContext(internal.WithArtifactInfo(req.Context(), afInfo)))
h := Middleware()(n)
h.ServeHTTP(rr, req)

View File

@ -3,23 +3,23 @@ package middleware
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"regexp"
"github.com/docker/distribution/reference"
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/promgr"
"github.com/goharbor/harbor/src/internal"
"github.com/goharbor/harbor/src/pkg/scan/vuln"
"github.com/goharbor/harbor/src/pkg/scan/whitelist"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"net/http"
"net/http/httptest"
"regexp"
)
type contextKey string
const (
// RepositorySubexp is the name for sub regex that maps to repository name in the url
RepositorySubexp = "repository"
@ -27,8 +27,6 @@ const (
ReferenceSubexp = "reference"
// DigestSubexp is the name for sub regex that maps to digest in the url
DigestSubexp = "digest"
// ArtifactInfoKey the context key for artifact info
ArtifactInfoKey = contextKey("artifactInfo")
)
var (
@ -44,33 +42,12 @@ var (
V2CatalogURLRe = regexp.MustCompile(`^/v2/_catalog$`)
)
// ArtifactInfo ...
type ArtifactInfo struct {
Repository string
Reference string
ProjectName string
Digest string
Tag string
BlobMountRepository string
BlobMountProjectName string
BlobMountDigest string
}
// ArtifactInfoFromContext returns the artifact info from context, the returned value is a copied value, so updating
// the attributes of returned artifactInfo will not update the one in context.
func ArtifactInfoFromContext(ctx context.Context) (ArtifactInfo, bool) {
info, ok := ctx.Value(ArtifactInfoKey).(*ArtifactInfo)
var res ArtifactInfo
if ok {
res = *info
}
return res, ok
}
// EnsureArtifactDigest get artifactInfo from context and set the digest for artifact that has project name repository and reference
func EnsureArtifactDigest(ctx context.Context) error {
info, ok := ctx.Value(ArtifactInfoKey).(*ArtifactInfo)
if !ok {
info := internal.GetArtifactInfo(ctx)
none := internal.ArtifactInfo{}
if info == none {
return fmt.Errorf("no artifact info in context")
}
if len(info.Digest) > 0 {

View File

@ -17,7 +17,6 @@ package v2auth
import (
"errors"
"fmt"
serror "github.com/goharbor/harbor/src/server/error"
"net/http"
"sync"
@ -26,7 +25,9 @@ import (
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/promgr"
"github.com/goharbor/harbor/src/internal"
ierror "github.com/goharbor/harbor/src/internal/error"
serror "github.com/goharbor/harbor/src/server/error"
"github.com/goharbor/harbor/src/server/middleware"
)
@ -43,7 +44,8 @@ func (rc *reqChecker) check(req *http.Request) error {
if !ok {
return fmt.Errorf("the security context got from request is nil")
}
if a, ok := middleware.ArtifactInfoFromContext(req.Context()); ok {
none := internal.ArtifactInfo{}
if a := internal.GetArtifactInfo(req.Context()); a != none {
action := getAction(req)
if action == "" {
return nil

View File

@ -27,8 +27,8 @@ import (
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/core/promgr/metamgr"
"github.com/goharbor/harbor/src/internal"
"github.com/goharbor/harbor/src/pkg/permission/types"
"github.com/goharbor/harbor/src/server/middleware"
securitytesting "github.com/goharbor/harbor/src/testing/common/security"
"github.com/goharbor/harbor/src/testing/mock"
"github.com/stretchr/testify/assert"
@ -116,17 +116,17 @@ func TestMiddleware(t *testing.T) {
})
baseCtx := security.NewContext(context.Background(), sc)
ar1 := &middleware.ArtifactInfo{
ar1 := internal.ArtifactInfo{
Repository: "project_1/hello-world",
Reference: "v1",
ProjectName: "project_1",
}
ar2 := &middleware.ArtifactInfo{
ar2 := internal.ArtifactInfo{
Repository: "library/ubuntu",
Reference: "14.04",
ProjectName: "library",
}
ar3 := &middleware.ArtifactInfo{
ar3 := internal.ArtifactInfo{
Repository: "project_1/ubuntu",
Reference: "14.04",
ProjectName: "project_1",
@ -134,7 +134,7 @@ func TestMiddleware(t *testing.T) {
BlobMountProjectName: "project_2",
BlobMountDigest: "sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f",
}
ar4 := &middleware.ArtifactInfo{
ar4 := internal.ArtifactInfo{
Repository: "project_1/ubuntu",
Reference: "14.04",
ProjectName: "project_1",
@ -142,7 +142,7 @@ func TestMiddleware(t *testing.T) {
BlobMountProjectName: "project_3",
BlobMountDigest: "sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f",
}
ar5 := &middleware.ArtifactInfo{
ar5 := internal.ArtifactInfo{
Repository: "project_1/ubuntu",
Reference: "14.04",
ProjectName: "project_1",
@ -151,12 +151,12 @@ func TestMiddleware(t *testing.T) {
BlobMountDigest: "sha256:08e4a417ff4e3913d8723a05cc34055db01c2fd165b588e049c5bad16ce6094f",
}
ctx1 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar1)
ctx2 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar2)
ctx2x := context.WithValue(context.Background(), middleware.ArtifactInfoKey, ar2) // no securityCtx
ctx3 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar3)
ctx4 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar4)
ctx5 := context.WithValue(baseCtx, middleware.ArtifactInfoKey, ar5)
ctx1 := internal.WithArtifactInfo(baseCtx, ar1)
ctx2 := internal.WithArtifactInfo(baseCtx, ar2)
ctx2x := internal.WithArtifactInfo(context.Background(), ar2) // no securityCtx
ctx3 := internal.WithArtifactInfo(baseCtx, ar3)
ctx4 := internal.WithArtifactInfo(baseCtx, ar4)
ctx5 := internal.WithArtifactInfo(baseCtx, ar5)
req1a, _ := http.NewRequest(http.MethodGet, "/v2/project_1/hello-world/manifest/v1", nil)
req1b, _ := http.NewRequest(http.MethodDelete, "/v2/project_1/hello-world/manifest/v1", nil)
req2, _ := http.NewRequest(http.MethodGet, "/v2/library/ubuntu/manifest/14.04", nil)

View File

@ -1,16 +1,17 @@
package vulnerable
import (
"github.com/goharbor/harbor/src/api/project"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/security"
"net/http"
"net/http/httptest"
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/api/project"
sc "github.com/goharbor/harbor/src/api/scan"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/internal"
internal_errors "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/scan/report"
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
@ -90,16 +91,16 @@ func Middleware() func(http.Handler) http.Handler {
}
}
func validate(req *http.Request) (bool, middleware.ArtifactInfo, vuln.Severity, models.CVEWhitelist) {
func validate(req *http.Request) (bool, internal.ArtifactInfo, vuln.Severity, models.CVEWhitelist) {
var vs vuln.Severity
var wl models.CVEWhitelist
var af middleware.ArtifactInfo
var none internal.ArtifactInfo
err := middleware.EnsureArtifactDigest(req.Context())
if err != nil {
return false, af, vs, wl
return false, none, vs, wl
}
af, ok := middleware.ArtifactInfoFromContext(req.Context())
if !ok {
af := internal.GetArtifactInfo(req.Context())
if af == none {
return false, af, vs, wl
}