mirror of
https://github.com/harness/drone.git
synced 2025-05-04 04:11:18 +08:00
feat: [AH-309]: Artifact Registry integration (#2318)
This commit is contained in:
parent
fddea6392f
commit
40ee05ca11
8
.gitignore
vendored
8
.gitignore
vendored
@ -8,6 +8,8 @@ _research
|
|||||||
web/node_modules
|
web/node_modules
|
||||||
web/dist
|
web/dist
|
||||||
web/coverage
|
web/coverage
|
||||||
|
web/.yalc
|
||||||
|
web/yalc.lock
|
||||||
yarn-error*
|
yarn-error*
|
||||||
release
|
release
|
||||||
.idea
|
.idea
|
||||||
@ -18,6 +20,12 @@ web/cypress/node_modules
|
|||||||
*.rsa
|
*.rsa
|
||||||
*.rsa.pub
|
*.rsa.pub
|
||||||
node_modules/
|
node_modules/
|
||||||
|
dist
|
||||||
|
.yalc
|
||||||
|
yalc.lock
|
||||||
|
node_modules
|
||||||
|
|
||||||
# ignore any executables we build
|
# ignore any executables we build
|
||||||
/gitness
|
/gitness
|
||||||
|
/registry/logs/*
|
||||||
|
/distribution-spec
|
||||||
|
188
.golangci.yml
188
.golangci.yml
@ -287,6 +287,8 @@ issues:
|
|||||||
linters: [ govet ]
|
linters: [ govet ]
|
||||||
- source: "^//\\s*go:generate\\s"
|
- source: "^//\\s*go:generate\\s"
|
||||||
linters: [ lll ]
|
linters: [ lll ]
|
||||||
|
- text: 'local replacement are not allowed: github.com/harness/gitness'
|
||||||
|
linters: [ gomoddirectives ]
|
||||||
- text: 'replacement are not allowed: github.com/docker/docker'
|
- text: 'replacement are not allowed: github.com/docker/docker'
|
||||||
linters: [ gomoddirectives ]
|
linters: [ gomoddirectives ]
|
||||||
- source: "(noinspection|TODO)"
|
- source: "(noinspection|TODO)"
|
||||||
@ -297,6 +299,192 @@ issues:
|
|||||||
linters: [ errorlint ]
|
linters: [ errorlint ]
|
||||||
- path: "^cli/"
|
- path: "^cli/"
|
||||||
linters: [forbidigo]
|
linters: [forbidigo]
|
||||||
|
#Registry Specific
|
||||||
|
- path: "^registry/app/manifest/.*"
|
||||||
|
linters: [ tagliatelle, staticcheck, revive ]
|
||||||
|
- path: "^registry/app/dist_temp/.*"
|
||||||
|
linters: [ errorlint ]
|
||||||
|
- path: "^registry/app/driver/filesystem/.*"
|
||||||
|
linters: [ gocritic ]
|
||||||
|
- path: "^registry/app/driver/s3-aws/.*"
|
||||||
|
linters: [ gocognit, gocyclo, gosec, nestif, cyclop]
|
||||||
|
- path: "^registry/app/remote/clients/registry/interceptor/interceptor.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/http/modifier/modifier.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/fileinfo.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/storagedriver.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/walk.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/challenge/addr.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/challenge/authchallenge.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/challenge/authchallenge_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/requestutil/util.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/requestutil/util_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/descriptor.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/doc.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/errors.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/manifests.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/versioned.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/lib/authorizer.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/lib/link.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/http/tls.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/http/transport.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/http/transport_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/schema2/manifest.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/schema2/manifest_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/ocischema/index.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/ocischema/manifest.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/auth/null/authorizer.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/auth/basic/authorizer.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/auth/basic/authorizer_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/lib/errors/const.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/lib/errors/errors.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/lib/errors/stack.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/common/lib/errors/stack_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/auth/bearer/authorizer.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/auth/bearer/cache.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/auth/bearer/scope.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/manifestlist/manifestlist.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/manifest/manifestlist/manifestlist_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/factory/factory.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/context.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/doc.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/http.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/logger.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/trace.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/util.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/version.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/http_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/trace_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/dcontext/version_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/base/base.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/base/regulator.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/base/regulator_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/blobs.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/blobwriter.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/blobwriter_resumable.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/errors.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/filereader.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/gcstoragelient.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/io.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/middleware.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/ociblobstore.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/paths.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/storage/storageservice.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/client.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/adapter/adapter.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/clients/registry/auth/authorizer.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/s3-aws/s3.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/s3-aws/s3_v2_signer.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/filesystem/driver.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/app.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/catalog.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/compat.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/context.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/controller.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/local.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/manifest_service.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/pkg/docker/remote.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/adapter/dockerhub/adapter.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/adapter/dockerhub/client.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/adapter/dockerhub/consts.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/driver/testsuites/testsuites.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/errcode/errors.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/errcode/handler.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/dist_temp/errcode/register.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/controller/proxy/controller.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/controller/proxy/inflight.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/controller/proxy/local.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/controller/proxy/remote.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/controller/proxy/inflight_test.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
- path: "^registry/app/remote/adapter/native/adapter.go"
|
||||||
|
linters: [ goheader ]
|
||||||
|
#Registry Specific ends
|
||||||
- text: "mnd: Magic number: \\d"
|
- text: "mnd: Magic number: \\d"
|
||||||
linters:
|
linters:
|
||||||
- gomnd
|
- gomnd
|
||||||
|
@ -10,3 +10,8 @@ GITNESS_DEBUG=true
|
|||||||
GITNESS_DOCKER_API_VERSION=1.41
|
GITNESS_DOCKER_API_VERSION=1.41
|
||||||
GITNESS_SSH_ENABLE=true
|
GITNESS_SSH_ENABLE=true
|
||||||
GITNESS_SSH_HOST=localhost
|
GITNESS_SSH_HOST=localhost
|
||||||
|
GITNESS_SSH_PORT=2222
|
||||||
|
|
||||||
|
GITNESS_REGISTRY_STORAGE_TYPE=filesystem
|
||||||
|
GITNESS_REGISTRY_ENABLED=false
|
||||||
|
GITNESS_REGISTRY_FILESYSTEM_ROOT_DIRECTORY=/tmp
|
||||||
|
39
Makefile
39
Makefile
@ -34,7 +34,7 @@ tools: $(tools) ## Install tools required for the build
|
|||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Build and testing rules
|
# Gitness Build and testing rules
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
@ -47,6 +47,43 @@ test: generate ## Run the go tests
|
|||||||
go test -v -coverprofile=coverage.out ./...
|
go test -v -coverprofile=coverage.out ./...
|
||||||
go tool cover -html=coverage.out
|
go tool cover -html=coverage.out
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Artifact Registry Build and testing rules
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
run: clean build
|
||||||
|
./gitness server .local.env || true
|
||||||
|
|
||||||
|
ar-conformance-test: clean build
|
||||||
|
./gitness server .local.env > logfile.log 2>&1 & echo $$! > server.PID
|
||||||
|
@sleep 10
|
||||||
|
./registry/tests/conformance_test.sh localhost:3000 || true
|
||||||
|
kill `cat server.PID`
|
||||||
|
@rm server.PID
|
||||||
|
@rm logfile.log
|
||||||
|
|
||||||
|
ar-hot-conformance-test:
|
||||||
|
rm -rf distribution-spec || true
|
||||||
|
./registry/tests/conformance_test.sh localhost:3000 || true
|
||||||
|
|
||||||
|
ar-api-update:
|
||||||
|
@set -e; \
|
||||||
|
oapi-codegen --config ./registry/config/openapi/artifact-services.yaml ./registry/app/api/openapi/api.yaml; \
|
||||||
|
oapi-codegen --config ./registry/config/openapi/artifact-types.yaml ./registry/app/api/openapi/api.yaml;
|
||||||
|
|
||||||
|
ar-clean:
|
||||||
|
@rm artifact-registry 2> /dev/null || true
|
||||||
|
@docker stop ps_artifacthub 2> /dev/null || true
|
||||||
|
rm -rf distribution-spec
|
||||||
|
@kill -9 $$(lsof -t -i:3000) || true
|
||||||
|
@rm server.PID || true
|
||||||
|
@rm logfile.log || true
|
||||||
|
go clean
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# Code Formatting and linting
|
# Code Formatting and linting
|
||||||
|
12
NOTICE
Normal file
12
NOTICE
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Copyright 2024 Harness, Inc.
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
|
||||||
|
https://github.com/goharbor/harbor
|
||||||
|
Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
https://github.com/distribution/distribution
|
||||||
|
Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
https://gitlab.com/gitlab-org/container-registry
|
||||||
|
Licensed under the Apache License, Version 2.0
|
@ -96,6 +96,14 @@ To regenerate the code, please execute the following steps:
|
|||||||
|
|
||||||
The latest API changes should now be reflected in `web/src/services/code/index.tsx`
|
The latest API changes should now be reflected in `web/src/services/code/index.tsx`
|
||||||
|
|
||||||
|
# Run Registry Conformance Tests
|
||||||
|
```
|
||||||
|
make conformance-test
|
||||||
|
```
|
||||||
|
For running conformance tests with existing running service, use:
|
||||||
|
```
|
||||||
|
make hot-conformance-test
|
||||||
|
```
|
||||||
|
|
||||||
## User Interface
|
## User Interface
|
||||||
|
|
||||||
@ -104,6 +112,7 @@ This project includes a full user interface for interacting with the system. Whe
|
|||||||
## REST API
|
## REST API
|
||||||
|
|
||||||
This project includes a swagger specification. When you run the application, you can access the swagger specification by navigating to `http://localhost:3000/swagger` in your browser (for raw yaml see `http://localhost:3000/openapi.yaml`).
|
This project includes a swagger specification. When you run the application, you can access the swagger specification by navigating to `http://localhost:3000/swagger` in your browser (for raw yaml see `http://localhost:3000/openapi.yaml`).
|
||||||
|
For registry endpoints, currently swagger is located on different endpoint `http://localhost:3000/registry/swagger/` (for raw json see `http://localhost:3000/registry/swagger.json`). These will be later moved to the main swagger endpoint.
|
||||||
|
|
||||||
|
|
||||||
For testing, it's simplest to just use the cli to create a token (this requires gitness server to run):
|
For testing, it's simplest to just use the cli to create a token (this requires gitness server to run):
|
||||||
|
@ -38,7 +38,8 @@ var (
|
|||||||
// Check checks if a resource specific permission is granted for the current auth session in the scope.
|
// Check checks if a resource specific permission is granted for the current auth session in the scope.
|
||||||
// Returns nil if the permission is granted, otherwise returns an error.
|
// Returns nil if the permission is granted, otherwise returns an error.
|
||||||
// NotAuthenticated, NotAuthorized, or any underlying error.
|
// NotAuthenticated, NotAuthorized, or any underlying error.
|
||||||
func Check(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
func Check(
|
||||||
|
ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
||||||
scope *types.Scope, resource *types.Resource, permission enum.Permission,
|
scope *types.Scope, resource *types.Resource, permission enum.Permission,
|
||||||
) error {
|
) error {
|
||||||
authorized, err := authorizer.Check(
|
authorized, err := authorizer.Check(
|
||||||
@ -46,7 +47,31 @@ func Check(ctx context.Context, authorizer authz.Authorizer, session *auth.Sessi
|
|||||||
session,
|
session,
|
||||||
scope,
|
scope,
|
||||||
resource,
|
resource,
|
||||||
permission)
|
permission,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !authorized {
|
||||||
|
return ErrNotAuthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckAll checks if multiple resources specific permission is granted for the current auth session in the scope.
|
||||||
|
// Returns nil if the permission is granted, otherwise returns an error.
|
||||||
|
// NotAuthenticated, NotAuthorized, or any underlying error.
|
||||||
|
func CheckAll(
|
||||||
|
ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
||||||
|
permissionChecks ...types.PermissionCheck,
|
||||||
|
) error {
|
||||||
|
authorized, err := authorizer.CheckAll(
|
||||||
|
ctx,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -62,9 +87,11 @@ func Check(ctx context.Context, authorizer authz.Authorizer, session *auth.Sessi
|
|||||||
// in the scope of a parent.
|
// in the scope of a parent.
|
||||||
// Returns nil if the permission is granted, otherwise returns an error.
|
// Returns nil if the permission is granted, otherwise returns an error.
|
||||||
// NotAuthenticated, NotAuthorized, or any underlying error.
|
// NotAuthenticated, NotAuthorized, or any underlying error.
|
||||||
func CheckChild(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
func CheckChild(
|
||||||
|
ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
||||||
spaceStore store.SpaceStore, repoStore store.RepoStore, parentType enum.ParentResourceType, parentID int64,
|
spaceStore store.SpaceStore, repoStore store.RepoStore, parentType enum.ParentResourceType, parentID int64,
|
||||||
resourceType enum.ResourceType, resourceName string, permission enum.Permission) error {
|
resourceType enum.ResourceType, resourceName string, permission enum.Permission,
|
||||||
|
) error {
|
||||||
scope, err := getScopeForParent(ctx, spaceStore, repoStore, parentType, parentID)
|
scope, err := getScopeForParent(ctx, spaceStore, repoStore, parentType, parentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -79,8 +106,10 @@ func CheckChild(ctx context.Context, authorizer authz.Authorizer, session *auth.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getScopeForParent Returns the scope for a given resource parent (space or repo).
|
// getScopeForParent Returns the scope for a given resource parent (space or repo).
|
||||||
func getScopeForParent(ctx context.Context, spaceStore store.SpaceStore, repoStore store.RepoStore,
|
func getScopeForParent(
|
||||||
parentType enum.ParentResourceType, parentID int64) (*types.Scope, error) {
|
ctx context.Context, spaceStore store.SpaceStore, repoStore store.RepoStore,
|
||||||
|
parentType enum.ParentResourceType, parentID int64,
|
||||||
|
) (*types.Scope, error) {
|
||||||
// TODO: Can this be done cleaner?
|
// TODO: Can this be done cleaner?
|
||||||
switch parentType {
|
switch parentType {
|
||||||
case enum.ParentResourceTypeSpace:
|
case enum.ParentResourceTypeSpace:
|
||||||
|
36
app/api/auth/registry.go
Normal file
36
app/api/auth/registry.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/app/auth/authz"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckRegistry checks if a registry specific permission is granted for the current auth session
|
||||||
|
// in the scope of its parent.
|
||||||
|
// Returns nil if the permission is granted, otherwise returns an error.
|
||||||
|
// NotAuthenticated, NotAuthorized, or any underlying error.
|
||||||
|
func CheckRegistry(
|
||||||
|
ctx context.Context,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
session *auth.Session,
|
||||||
|
permissionChecks ...types.PermissionCheck,
|
||||||
|
) error {
|
||||||
|
return CheckAll(ctx, authorizer, session, permissionChecks...)
|
||||||
|
}
|
@ -126,7 +126,7 @@ func enc(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// helper function returns the same secret with decrypted data.
|
// helper function returns the same secret with decrypted data.
|
||||||
func dec(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
|
func Dec(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
|
||||||
if secret == nil {
|
if secret == nil {
|
||||||
return nil, fmt.Errorf("cannot decrypt a nil secret")
|
return nil, fmt.Errorf("cannot decrypt a nil secret")
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func (c *Controller) Find(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to find secret: %w", err)
|
return nil, fmt.Errorf("failed to find secret: %w", err)
|
||||||
}
|
}
|
||||||
secret, err = dec(c.encrypter, secret)
|
secret, err = Dec(c.encrypter, secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not decrypt secret: %w", err)
|
return nil, fmt.Errorf("could not decrypt secret: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func (c *Controller) Login(
|
|||||||
return nil, usererror.ErrNotFound
|
return nil, usererror.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenIdentifier, err := generateSessionTokenIdentifier()
|
tokenIdentifier, err := GenerateSessionTokenIdentifier()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ func (c *Controller) Login(
|
|||||||
return &types.TokenResponse{Token: *token, AccessToken: jwtToken}, nil
|
return &types.TokenResponse{Token: *token, AccessToken: jwtToken}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateSessionTokenIdentifier() (string, error) {
|
func GenerateSessionTokenIdentifier() (string, error) {
|
||||||
r, err := rand.Int(rand.Reader, big.NewInt(10000))
|
r, err := rand.Int(rand.Reader, big.NewInt(10000))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to generate random number: %w", err)
|
return "", fmt.Errorf("failed to generate random number: %w", err)
|
||||||
|
@ -27,6 +27,7 @@ type ConfigOutput struct {
|
|||||||
PublicResourceCreationEnabled bool `json:"public_resource_creation_enabled"`
|
PublicResourceCreationEnabled bool `json:"public_resource_creation_enabled"`
|
||||||
SSHEnabled bool `json:"ssh_enabled"`
|
SSHEnabled bool `json:"ssh_enabled"`
|
||||||
GitspaceEnabled bool `json:"gitspace_enabled"`
|
GitspaceEnabled bool `json:"gitspace_enabled"`
|
||||||
|
ArtifactRegistryEnabled bool `json:"artifact_registry_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleGetConfig returns an http.HandlerFunc that processes an http.Request
|
// HandleGetConfig returns an http.HandlerFunc that processes an http.Request
|
||||||
@ -46,6 +47,7 @@ func HandleGetConfig(config *types.Config, sysCtrl *system.Controller) http.Hand
|
|||||||
UserSignupAllowed: userSignupAllowed,
|
UserSignupAllowed: userSignupAllowed,
|
||||||
PublicResourceCreationEnabled: config.PublicResourceCreationEnabled,
|
PublicResourceCreationEnabled: config.PublicResourceCreationEnabled,
|
||||||
GitspaceEnabled: config.Gitspace.Enable,
|
GitspaceEnabled: config.Gitspace.Enable,
|
||||||
|
ArtifactRegistryEnabled: config.Registry.Enable,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,21 +28,23 @@ import (
|
|||||||
// BlockSessionToken blocks any request that uses a session token for authentication.
|
// BlockSessionToken blocks any request that uses a session token for authentication.
|
||||||
// NOTE: Major use case as of now is blocking usage of session tokens with git.
|
// NOTE: Major use case as of now is blocking usage of session tokens with git.
|
||||||
func BlockSessionToken(next http.Handler) http.Handler {
|
func BlockSessionToken(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(
|
||||||
ctx := r.Context()
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
// only block if auth data was available and it's based on a session token.
|
// only block if auth data was available and it's based on a session token.
|
||||||
if session, oks := request.AuthSessionFrom(ctx); oks {
|
if session, oks := request.AuthSessionFrom(ctx); oks {
|
||||||
if tokenMetadata, okt := session.Metadata.(*auth.TokenMetadata); okt &&
|
if tokenMetadata, ok := session.Metadata.(*auth.TokenMetadata); ok &&
|
||||||
tokenMetadata.TokenType == enum.TokenTypeSession {
|
tokenMetadata.TokenType == enum.TokenTypeSession {
|
||||||
log.Ctx(ctx).Warn().Msg("blocking git operation - session tokens are not allowed for usage with git")
|
log.Ctx(ctx).Warn().Msg("blocking git operation - session tokens are not allowed for usage with git")
|
||||||
|
|
||||||
// NOTE: Git doesn't print the error message, so just return default 401 Unauthorized.
|
// NOTE: Git doesn't print the error message, so just return default 401 Unauthorized.
|
||||||
render.Unauthorized(ctx, w)
|
render.Unauthorized(ctx, w)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -62,13 +62,15 @@ func (a *JWTAuthenticator) Authenticate(r *http.Request) (*auth.Session, error)
|
|||||||
var principal *types.Principal
|
var principal *types.Principal
|
||||||
var err error
|
var err error
|
||||||
claims := &jwt.Claims{}
|
claims := &jwt.Claims{}
|
||||||
parsed, err := gojwt.ParseWithClaims(str, claims, func(_ *gojwt.Token) (interface{}, error) {
|
parsed, err := gojwt.ParseWithClaims(
|
||||||
principal, err = a.principalStore.Find(ctx, claims.PrincipalID)
|
str, claims, func(_ *gojwt.Token) (interface{}, error) {
|
||||||
if err != nil {
|
principal, err = a.principalStore.Find(ctx, claims.PrincipalID)
|
||||||
return nil, fmt.Errorf("failed to get principal for token: %w", err)
|
if err != nil {
|
||||||
}
|
return nil, fmt.Errorf("failed to get principal for token: %w", err)
|
||||||
return []byte(principal.Salt), nil
|
}
|
||||||
})
|
return []byte(principal.Salt), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing of JWT claims failed: %w", err)
|
return nil, fmt.Errorf("parsing of JWT claims failed: %w", err)
|
||||||
}
|
}
|
||||||
@ -90,6 +92,8 @@ func (a *JWTAuthenticator) Authenticate(r *http.Request) (*auth.Session, error)
|
|||||||
}
|
}
|
||||||
case claims.Membership != nil:
|
case claims.Membership != nil:
|
||||||
metadata = a.metadataFromMembershipClaims(claims.Membership)
|
metadata = a.metadataFromMembershipClaims(claims.Membership)
|
||||||
|
case claims.AccessPermissions != nil:
|
||||||
|
metadata = a.metadataFromAccessPermissions(claims.AccessPermissions)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("jwt is missing sub-claims")
|
return nil, fmt.Errorf("jwt is missing sub-claims")
|
||||||
}
|
}
|
||||||
@ -113,8 +117,10 @@ func (a *JWTAuthenticator) metadataFromTokenClaims(
|
|||||||
|
|
||||||
// protect against faked JWTs for other principals in case of single salt leak
|
// protect against faked JWTs for other principals in case of single salt leak
|
||||||
if principal.ID != tkn.PrincipalID {
|
if principal.ID != tkn.PrincipalID {
|
||||||
return nil, fmt.Errorf("JWT was for principal %d while db token was for principal %d",
|
return nil, fmt.Errorf(
|
||||||
principal.ID, tkn.PrincipalID)
|
"JWT was for principal %d while db token was for principal %d",
|
||||||
|
principal.ID, tkn.PrincipalID,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &auth.TokenMetadata{
|
return &auth.TokenMetadata{
|
||||||
@ -133,6 +139,14 @@ func (a *JWTAuthenticator) metadataFromMembershipClaims(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *JWTAuthenticator) metadataFromAccessPermissions(
|
||||||
|
s *jwt.SubClaimsAccessPermissions,
|
||||||
|
) auth.Metadata {
|
||||||
|
return &auth.AccessPermissionMetadata{
|
||||||
|
AccessPermissions: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func extractToken(r *http.Request, cookieName string) string {
|
func extractToken(r *http.Request, cookieName string) string {
|
||||||
// Check query param first (as that's most immediately visible to caller)
|
// Check query param first (as that's most immediately visible to caller)
|
||||||
if queryToken, ok := request.GetAccessTokenFromQuery(r); ok {
|
if queryToken, ok := request.GetAccessTokenFromQuery(r); ok {
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/harness/gitness/types/enum"
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Authorizer = (*MembershipAuthorizer)(nil)
|
var _ Authorizer = (*MembershipAuthorizer)(nil)
|
||||||
@ -110,6 +111,9 @@ func (a *MembershipAuthorizer) Check(
|
|||||||
case enum.ResourceTypeInfraProvider:
|
case enum.ResourceTypeInfraProvider:
|
||||||
spacePath = scope.SpacePath
|
spacePath = scope.SpacePath
|
||||||
|
|
||||||
|
case enum.ResourceTypeRegistry:
|
||||||
|
spacePath = scope.SpacePath
|
||||||
|
|
||||||
case enum.ResourceTypeUser:
|
case enum.ResourceTypeUser:
|
||||||
// a user is allowed to edit themselves
|
// a user is allowed to edit themselves
|
||||||
if resource.Identifier == session.Principal.UID &&
|
if resource.Identifier == session.Principal.UID &&
|
||||||
@ -138,20 +142,29 @@ func (a *MembershipAuthorizer) Check(
|
|||||||
return a.checkWithMembershipMetadata(ctx, membershipMetadata, spacePath, permission)
|
return a.checkWithMembershipMetadata(ctx, membershipMetadata, spacePath, permission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accessPermissionMetadata contains the access permissions of per space
|
||||||
|
if accessPermissionMetadata, ok := session.Metadata.(*auth.AccessPermissionMetadata); ok {
|
||||||
|
return a.checkWithAccessPermissionMetadata(ctx, accessPermissionMetadata, spacePath, permission)
|
||||||
|
}
|
||||||
|
|
||||||
// ensure we aren't bypassing unknown metadata with impact on authorization
|
// ensure we aren't bypassing unknown metadata with impact on authorization
|
||||||
if session.Metadata != nil && session.Metadata.ImpactsAuthorization() {
|
if session.Metadata != nil && session.Metadata.ImpactsAuthorization() {
|
||||||
return false, fmt.Errorf("session contains unknown metadata that impacts authorization: %T", session.Metadata)
|
return false, fmt.Errorf("session contains unknown metadata that impacts authorization: %T", session.Metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.permissionCache.Get(ctx, PermissionCacheKey{
|
return a.permissionCache.Get(
|
||||||
PrincipalID: session.Principal.ID,
|
ctx, PermissionCacheKey{
|
||||||
SpaceRef: spacePath,
|
PrincipalID: session.Principal.ID,
|
||||||
Permission: permission,
|
SpaceRef: spacePath,
|
||||||
})
|
Permission: permission,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *MembershipAuthorizer) CheckAll(ctx context.Context, session *auth.Session,
|
func (a *MembershipAuthorizer) CheckAll(
|
||||||
permissionChecks ...types.PermissionCheck) (bool, error) {
|
ctx context.Context, session *auth.Session,
|
||||||
|
permissionChecks ...types.PermissionCheck,
|
||||||
|
) (bool, error) {
|
||||||
for i := range permissionChecks {
|
for i := range permissionChecks {
|
||||||
p := permissionChecks[i]
|
p := permissionChecks[i]
|
||||||
if _, err := a.Check(ctx, session, &p.Scope, &p.Resource, p.Permission); err != nil {
|
if _, err := a.Check(ctx, session, &p.Scope, &p.Resource, p.Permission); err != nil {
|
||||||
@ -193,3 +206,28 @@ func (a *MembershipAuthorizer) checkWithMembershipMetadata(
|
|||||||
// access is granted by ephemeral membership
|
// access is granted by ephemeral membership
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkWithAccessPermissionMetadata checks access using the ephemeral membership provided in the metadata.
|
||||||
|
func (a *MembershipAuthorizer) checkWithAccessPermissionMetadata(
|
||||||
|
ctx context.Context,
|
||||||
|
accessPermissionMetadata *auth.AccessPermissionMetadata,
|
||||||
|
requestedSpacePath string,
|
||||||
|
requestedPermission enum.Permission,
|
||||||
|
) (bool, error) {
|
||||||
|
space, err := a.spaceStore.FindByRef(ctx, requestedSpacePath)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("failed to find space by ref: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessPermissionMetadata.AccessPermissions.Permissions == nil {
|
||||||
|
return false, fmt.Errorf("no %s permission provided", requestedPermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, accessPermission := range accessPermissionMetadata.AccessPermissions.Permissions {
|
||||||
|
if space.ID == accessPermission.SpaceID && slices.Contains(accessPermission.Permissions, requestedPermission) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, fmt.Errorf("no %s permission provided", requestedPermission)
|
||||||
|
}
|
||||||
|
@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import "github.com/harness/gitness/types/enum"
|
import (
|
||||||
|
"github.com/harness/gitness/app/jwt"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
type Metadata interface {
|
type Metadata interface {
|
||||||
ImpactsAuthorization() bool
|
ImpactsAuthorization() bool
|
||||||
@ -46,3 +49,12 @@ type MembershipMetadata struct {
|
|||||||
func (m *MembershipMetadata) ImpactsAuthorization() bool {
|
func (m *MembershipMetadata) ImpactsAuthorization() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccessPermissionMetadata contains information about permissions per space.
|
||||||
|
type AccessPermissionMetadata struct {
|
||||||
|
AccessPermissions *jwt.SubClaimsAccessPermissions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AccessPermissionMetadata) ImpactsAuthorization() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -15,27 +15,35 @@
|
|||||||
package jwt
|
package jwt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
"github.com/harness/gitness/types/enum"
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
issuer = "Gitness"
|
issuer = "Gitness"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Source represents the source of the SubClaimsAccessPermissions.
|
||||||
|
type Source string
|
||||||
|
|
||||||
|
const (
|
||||||
|
OciSource Source = "oci"
|
||||||
|
)
|
||||||
|
|
||||||
// Claims defines gitness jwt claims.
|
// Claims defines gitness jwt claims.
|
||||||
type Claims struct {
|
type Claims struct {
|
||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
|
|
||||||
PrincipalID int64 `json:"pid,omitempty"`
|
PrincipalID int64 `json:"pid,omitempty"`
|
||||||
|
|
||||||
Token *SubClaimsToken `json:"tkn,omitempty"`
|
Token *SubClaimsToken `json:"tkn,omitempty"`
|
||||||
Membership *SubClaimsMembership `json:"ms,omitempty"`
|
Membership *SubClaimsMembership `json:"ms,omitempty"`
|
||||||
|
AccessPermissions *SubClaimsAccessPermissions `json:"ap,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubClaimsToken contains information about the token the JWT was created for.
|
// SubClaimsToken contains information about the token the JWT was created for.
|
||||||
@ -50,6 +58,18 @@ type SubClaimsMembership struct {
|
|||||||
SpaceID int64 `json:"sid,omitempty"`
|
SpaceID int64 `json:"sid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubClaimsAccessPermissions stores allowed actions on a resource.
|
||||||
|
type SubClaimsAccessPermissions struct {
|
||||||
|
Source Source `json:"src,omitempty"`
|
||||||
|
Permissions []AccessPermissions `json:"permissions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessPermissions stores allowed actions on a resource.
|
||||||
|
type AccessPermissions struct {
|
||||||
|
SpaceID int64 `json:"sid,omitempty"`
|
||||||
|
Permissions []enum.Permission `json:"p"`
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateForToken generates a jwt for a given token.
|
// GenerateForToken generates a jwt for a given token.
|
||||||
func GenerateForToken(token *types.Token, secret string) (string, error) {
|
func GenerateForToken(token *types.Token, secret string) (string, error) {
|
||||||
var expiresAt int64
|
var expiresAt int64
|
||||||
@ -73,7 +93,7 @@ func GenerateForToken(token *types.Token, secret string) (string, error) {
|
|||||||
|
|
||||||
res, err := jwtToken.SignedString([]byte(secret))
|
res, err := jwtToken.SignedString([]byte(secret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "Failed to sign token")
|
return "", fmt.Errorf("failed to sign token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
@ -106,7 +126,37 @@ func GenerateWithMembership(
|
|||||||
|
|
||||||
res, err := jwtToken.SignedString([]byte(secret))
|
res, err := jwtToken.SignedString([]byte(secret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "Failed to sign token")
|
return "", fmt.Errorf("failed to sign token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateForTokenWithAccessPermissions generates a jwt for a given token.
|
||||||
|
func GenerateForTokenWithAccessPermissions(
|
||||||
|
principalID int64,
|
||||||
|
lifetime *time.Duration,
|
||||||
|
secret string, accessPermissions *SubClaimsAccessPermissions,
|
||||||
|
) (string, error) {
|
||||||
|
issuedAt := time.Now()
|
||||||
|
if lifetime == nil {
|
||||||
|
return "", fmt.Errorf("token lifetime is required")
|
||||||
|
}
|
||||||
|
expiresAt := issuedAt.Add(*lifetime)
|
||||||
|
|
||||||
|
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{
|
||||||
|
StandardClaims: jwt.StandardClaims{
|
||||||
|
Issuer: issuer,
|
||||||
|
IssuedAt: issuedAt.Unix(),
|
||||||
|
ExpiresAt: expiresAt.Unix(),
|
||||||
|
},
|
||||||
|
PrincipalID: principalID,
|
||||||
|
AccessPermissions: accessPermissions,
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := jwtToken.SignedString([]byte(secret))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to sign token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
@ -48,6 +48,8 @@ import (
|
|||||||
"github.com/harness/gitness/app/auth/authn"
|
"github.com/harness/gitness/app/auth/authn"
|
||||||
"github.com/harness/gitness/app/url"
|
"github.com/harness/gitness/app/url"
|
||||||
"github.com/harness/gitness/git"
|
"github.com/harness/gitness/git"
|
||||||
|
"github.com/harness/gitness/registry/app/api"
|
||||||
|
"github.com/harness/gitness/registry/app/api/router"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
@ -56,6 +58,7 @@ import (
|
|||||||
// WireSet provides a wire set for this package.
|
// WireSet provides a wire set for this package.
|
||||||
var WireSet = wire.NewSet(
|
var WireSet = wire.NewSet(
|
||||||
ProvideRouter,
|
ProvideRouter,
|
||||||
|
api.WireSet,
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetGitRoutingHost(ctx context.Context, urlProvider url.Provider) string {
|
func GetGitRoutingHost(ctx context.Context, urlProvider url.Provider) string {
|
||||||
@ -106,8 +109,9 @@ func ProvideRouter(
|
|||||||
capabilitiesCtrl *capabilities.Controller,
|
capabilitiesCtrl *capabilities.Controller,
|
||||||
urlProvider url.Provider,
|
urlProvider url.Provider,
|
||||||
openapi openapi.Service,
|
openapi openapi.Service,
|
||||||
|
registryRouter router.AppRouter,
|
||||||
) *Router {
|
) *Router {
|
||||||
routers := make([]Interface, 3)
|
routers := make([]Interface, 4)
|
||||||
|
|
||||||
gitRoutingHost := GetGitRoutingHost(appCtx, urlProvider)
|
gitRoutingHost := GetGitRoutingHost(appCtx, urlProvider)
|
||||||
gitHandler := NewGitHandler(
|
gitHandler := NewGitHandler(
|
||||||
@ -116,16 +120,18 @@ func ProvideRouter(
|
|||||||
repoCtrl,
|
repoCtrl,
|
||||||
)
|
)
|
||||||
routers[0] = NewGitRouter(gitHandler, gitRoutingHost)
|
routers[0] = NewGitRouter(gitHandler, gitRoutingHost)
|
||||||
|
routers[1] = router.NewRegistryRouter(registryRouter)
|
||||||
|
|
||||||
apiHandler := NewAPIHandler(appCtx, config,
|
apiHandler := NewAPIHandler(
|
||||||
|
appCtx, config,
|
||||||
authenticator, repoCtrl, repoSettingsCtrl, executionCtrl, logCtrl, spaceCtrl, pipelineCtrl,
|
authenticator, repoCtrl, repoSettingsCtrl, executionCtrl, logCtrl, spaceCtrl, pipelineCtrl,
|
||||||
secretCtrl, triggerCtrl, connectorCtrl, templateCtrl, pluginCtrl, pullreqCtrl, webhookCtrl,
|
secretCtrl, triggerCtrl, connectorCtrl, templateCtrl, pluginCtrl, pullreqCtrl, webhookCtrl,
|
||||||
githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, blobCtrl, searchCtrl,
|
githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, blobCtrl, searchCtrl,
|
||||||
infraProviderCtrl, migrateCtrl, gitspaceCtrl, aiagentCtrl, capabilitiesCtrl)
|
infraProviderCtrl, migrateCtrl, gitspaceCtrl, aiagentCtrl, capabilitiesCtrl)
|
||||||
routers[1] = NewAPIRouter(apiHandler)
|
routers[2] = NewAPIRouter(apiHandler)
|
||||||
|
|
||||||
webHandler := NewWebHandler(config, authenticator, openapi)
|
webHandler := NewWebHandler(config, authenticator, openapi)
|
||||||
routers[2] = NewWebRouter(webHandler)
|
routers[3] = NewWebRouter(webHandler)
|
||||||
|
|
||||||
return NewRouter(routers)
|
return NewRouter(routers)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
DROP TABLE registries;
|
||||||
|
DROP TABLE media_types;
|
||||||
|
DROP TABLE blobs;
|
||||||
|
DROP TABLE registry_blobs;
|
||||||
|
DROP TABLE manifests;
|
||||||
|
DROP TABLE manifest_references;
|
||||||
|
DROP TABLE layers;
|
||||||
|
DROP TABLE artifacts;
|
||||||
|
DROP TABLE artifact_stats;
|
||||||
|
DROP TABLE tags;
|
||||||
|
DROP TABLE upstream_proxy_configs;
|
||||||
|
DROP TABLE cleanup_policies;
|
||||||
|
DROP TABLE cleanup_policy_prefix_mappings;
|
561
app/store/database/migrate/postgres/0066_create_ar_tables.up.sql
Normal file
561
app/store/database/migrate/postgres/0066_create_ar_tables.up.sql
Normal file
@ -0,0 +1,561 @@
|
|||||||
|
create table registries
|
||||||
|
(
|
||||||
|
registry_id SERIAL primary key,
|
||||||
|
registry_name text not null
|
||||||
|
constraint registry_name_len_check
|
||||||
|
check (length(registry_name) <= 255),
|
||||||
|
registry_root_parent_id INTEGER not null,
|
||||||
|
registry_parent_id INTEGER not null,
|
||||||
|
registry_description text,
|
||||||
|
registry_type text not null,
|
||||||
|
registry_package_type text not null,
|
||||||
|
registry_upstream_proxies text,
|
||||||
|
registry_allowed_pattern text,
|
||||||
|
registry_blocked_pattern text,
|
||||||
|
registry_created_at BIGINT not null,
|
||||||
|
registry_updated_at BIGINT not null,
|
||||||
|
registry_created_by INTEGER not null,
|
||||||
|
registry_updated_by INTEGER not null,
|
||||||
|
registry_labels text,
|
||||||
|
constraint unique_registries
|
||||||
|
unique (registry_root_parent_id, registry_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
create table media_types
|
||||||
|
(
|
||||||
|
mt_id SERIAL primary key,
|
||||||
|
mt_media_type text not null
|
||||||
|
constraint unique_media_types_type
|
||||||
|
unique,
|
||||||
|
mt_created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM now()) * 1000)::BIGINT
|
||||||
|
);
|
||||||
|
|
||||||
|
create table blobs
|
||||||
|
(
|
||||||
|
blob_id SERIAL primary key,
|
||||||
|
blob_root_parent_id INTEGER not null,
|
||||||
|
blob_digest bytea not null,
|
||||||
|
blob_media_type_id INTEGER not null
|
||||||
|
constraint fk_blobs_media_type_id_media_types
|
||||||
|
references media_types(mt_id),
|
||||||
|
blob_size BIGINT not null,
|
||||||
|
blob_created_at BIGINT not null,
|
||||||
|
blob_created_by INTEGER not null,
|
||||||
|
constraint unique_digest_root_parent_id unique (blob_digest, blob_root_parent_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_blobs_on_media_type_id
|
||||||
|
on blobs (blob_media_type_id);
|
||||||
|
|
||||||
|
create table registry_blobs
|
||||||
|
(
|
||||||
|
rblob_id SERIAL primary key,
|
||||||
|
rblob_registry_id INTEGER not null
|
||||||
|
constraint fk_registry_blobs_rpstry_id_registries
|
||||||
|
references registries
|
||||||
|
on delete cascade,
|
||||||
|
rblob_blob_id INTEGER not null
|
||||||
|
constraint fk_registry_blobs_blob_id_blobs
|
||||||
|
references blobs
|
||||||
|
on delete cascade,
|
||||||
|
rblob_image_name text
|
||||||
|
constraint registry_blobs_image_len_check
|
||||||
|
check (length(rblob_image_name) <= 255),
|
||||||
|
rblob_created_at BIGINT not null,
|
||||||
|
rblob_updated_at BIGINT not null,
|
||||||
|
rblob_created_by INTEGER not null,
|
||||||
|
rblob_updated_by INTEGER not null,
|
||||||
|
|
||||||
|
constraint unique_registry_blobs_registry_id_blob_id_image
|
||||||
|
unique (rblob_registry_id, rblob_blob_id, rblob_image_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_registry_blobs_on_reg_id
|
||||||
|
on registry_blobs (rblob_registry_id);
|
||||||
|
|
||||||
|
create index index_registry_blobs_on_reg_blob_id
|
||||||
|
on registry_blobs (rblob_registry_id, rblob_blob_id);
|
||||||
|
|
||||||
|
create table manifests
|
||||||
|
(
|
||||||
|
manifest_id SERIAL primary key,
|
||||||
|
manifest_registry_id INTEGER not null
|
||||||
|
constraint fk_manifests_registry_id_registries
|
||||||
|
references registries(registry_id)
|
||||||
|
on delete cascade,
|
||||||
|
manifest_schema_version smallint not null,
|
||||||
|
manifest_media_type_id INTEGER not null
|
||||||
|
constraint fk_manifests_media_type_id_media_types
|
||||||
|
references media_types(mt_id),
|
||||||
|
manifest_artifact_media_type text,
|
||||||
|
manifest_total_size BIGINT not null,
|
||||||
|
manifest_configuration_media_type text,
|
||||||
|
manifest_configuration_payload bytea,
|
||||||
|
manifest_configuration_blob_id INTEGER
|
||||||
|
constraint fk_manifests_configuration_blob_id_blobs
|
||||||
|
references blobs(blob_id),
|
||||||
|
manifest_configuration_digest bytea,
|
||||||
|
manifest_digest bytea not null,
|
||||||
|
manifest_payload bytea not null,
|
||||||
|
manifest_non_conformant boolean default false,
|
||||||
|
manifest_non_distributable_layers boolean default false,
|
||||||
|
manifest_subject_id INTEGER,
|
||||||
|
manifest_subject_digest bytea,
|
||||||
|
manifest_annotations bytea,
|
||||||
|
manifest_image_name text not null
|
||||||
|
constraint manifests_img_name_len_check
|
||||||
|
check (length(manifest_image_name) <= 255),
|
||||||
|
manifest_created_at BIGINT not null,
|
||||||
|
manifest_created_by INTEGER not null,
|
||||||
|
manifest_updated_at BIGINT not null,
|
||||||
|
manifest_updated_by INTEGER not null,
|
||||||
|
constraint unique_manifests_registry_id_image_name_and_digest
|
||||||
|
unique (manifest_registry_id, manifest_image_name, manifest_digest),
|
||||||
|
constraint unique_manifests_registry_id_id_cfg_blob_id
|
||||||
|
unique (manifest_registry_id, manifest_id, manifest_configuration_blob_id),
|
||||||
|
constraint fk_manifests_subject_id_manifests
|
||||||
|
foreign key (manifest_subject_id) references manifests
|
||||||
|
on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_manifests_on_media_type_id
|
||||||
|
on manifests (manifest_media_type_id);
|
||||||
|
|
||||||
|
create index index_manifests_on_configuration_blob_id
|
||||||
|
on manifests (manifest_configuration_blob_id);
|
||||||
|
|
||||||
|
create table manifest_references
|
||||||
|
(
|
||||||
|
manifest_ref_id SERIAL primary key,
|
||||||
|
manifest_ref_registry_id INTEGER not null,
|
||||||
|
manifest_ref_parent_id INTEGER not null,
|
||||||
|
manifest_ref_child_id INTEGER not null,
|
||||||
|
manifest_ref_created_at BIGINT not null,
|
||||||
|
manifest_ref_updated_at BIGINT not null,
|
||||||
|
manifest_ref_created_by INTEGER not null,
|
||||||
|
manifest_ref_updated_by INTEGER not null,
|
||||||
|
constraint unique_manifest_references_prt_id_chd_id
|
||||||
|
unique (manifest_ref_registry_id, manifest_ref_parent_id, manifest_ref_child_id),
|
||||||
|
constraint fk_manifest_references_parent_id_mnfsts
|
||||||
|
foreign key (manifest_ref_parent_id) references manifests
|
||||||
|
on delete cascade,
|
||||||
|
constraint fk_manifest_references_child_id_mnfsts
|
||||||
|
foreign key (manifest_ref_child_id) references manifests,
|
||||||
|
constraint check_manifest_references_parent_id_and_child_id_differ
|
||||||
|
check (manifest_ref_parent_id <> manifest_ref_child_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_manifest_references_on_rpstry_id_child_id
|
||||||
|
on manifest_references (manifest_ref_registry_id, manifest_ref_child_id);
|
||||||
|
|
||||||
|
create table layers
|
||||||
|
(
|
||||||
|
layer_id SERIAL primary key,
|
||||||
|
layer_registry_id INTEGER not null,
|
||||||
|
layer_manifest_id INTEGER not null,
|
||||||
|
layer_media_type_id INTEGER not null
|
||||||
|
constraint fk_layer_media_type_id_media_types
|
||||||
|
references media_types,
|
||||||
|
layer_blob_id INTEGER not null
|
||||||
|
constraint fk_layer_blob_id_blobs
|
||||||
|
references blobs,
|
||||||
|
layer_size BIGINT not null,
|
||||||
|
layer_created_at BIGINT not null,
|
||||||
|
layer_updated_at BIGINT not null,
|
||||||
|
layer_created_by INTEGER not null,
|
||||||
|
layer_updated_by INTEGER not null,
|
||||||
|
constraint unique_layer_rpstry_id_and_mnfst_id_and_blob_id
|
||||||
|
unique (layer_registry_id, layer_manifest_id, layer_blob_id),
|
||||||
|
constraint unique_layer_rpstry_id_and_id_and_blob_id
|
||||||
|
unique (layer_registry_id, layer_id, layer_blob_id),
|
||||||
|
constraint fk_manifst_id_manifests
|
||||||
|
foreign key (layer_manifest_id) references manifests(manifest_id)
|
||||||
|
on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_layer_on_media_type_id
|
||||||
|
on layers (layer_media_type_id);
|
||||||
|
|
||||||
|
create index index_layer_on_blob_id
|
||||||
|
on layers (layer_blob_id);
|
||||||
|
|
||||||
|
create table artifacts
|
||||||
|
(
|
||||||
|
artifact_id SERIAL primary key,
|
||||||
|
artifact_name text not null,
|
||||||
|
artifact_registry_id INTEGER not null
|
||||||
|
constraint fk_registries_registry_id
|
||||||
|
references registries(registry_id)
|
||||||
|
on delete cascade,
|
||||||
|
artifact_labels text,
|
||||||
|
artifact_enabled boolean default false,
|
||||||
|
artifact_created_at BIGINT,
|
||||||
|
artifact_updated_at BIGINT,
|
||||||
|
artifact_created_by INTEGER,
|
||||||
|
artifact_updated_by INTEGER,
|
||||||
|
constraint unique_artifact_registry_id_and_name unique (artifact_registry_id, artifact_name),
|
||||||
|
constraint check_artifact_name_length check ((char_length(artifact_name) <= 255))
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_artifact_on_registry_id ON artifacts USING btree (artifact_registry_id);
|
||||||
|
|
||||||
|
|
||||||
|
create table artifact_stats
|
||||||
|
(
|
||||||
|
artifact_stat_id SERIAL primary key,
|
||||||
|
artifact_stat_artifact_id INTEGER not null
|
||||||
|
constraint fk_artifacts_artifact_id
|
||||||
|
references artifacts(artifact_id),
|
||||||
|
artifact_stat_date BIGINT,
|
||||||
|
artifact_stat_download_count BIGINT,
|
||||||
|
artifact_stat_upload_bytes BIGINT,
|
||||||
|
artifact_stat_download_bytes BIGINT,
|
||||||
|
artifact_stat_created_at BIGINT not null,
|
||||||
|
artifact_stat_updated_at BIGINT not null,
|
||||||
|
artifact_stat_created_by INTEGER not null,
|
||||||
|
artifact_stat_updated_by INTEGER not null,
|
||||||
|
constraint unique_artifact_stats_artifact_id_and_date unique (artifact_stat_artifact_id, artifact_stat_date)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table tags
|
||||||
|
(
|
||||||
|
tag_id SERIAL primary key,
|
||||||
|
tag_name text not null
|
||||||
|
constraint tag_name_len_check
|
||||||
|
check (char_length(tag_name) <= 128),
|
||||||
|
tag_image_name text not null
|
||||||
|
constraint tag_img_name_len_check
|
||||||
|
check (length(tag_image_name) <= 255),
|
||||||
|
tag_registry_id INTEGER not null,
|
||||||
|
tag_manifest_id INTEGER not null,
|
||||||
|
tag_created_at BIGINT,
|
||||||
|
tag_updated_at BIGINT,
|
||||||
|
tag_created_by INTEGER,
|
||||||
|
tag_updated_by INTEGER,
|
||||||
|
constraint fk_tag_manifest_id_manifests FOREIGN KEY
|
||||||
|
(tag_manifest_id) REFERENCES manifests (manifest_id) ON DELETE CASCADE,
|
||||||
|
constraint unique_tag_registry_id_and_name_and_image_name
|
||||||
|
unique (tag_registry_id, tag_name, tag_image_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_tag_on_rpository_id_and_manifest_id
|
||||||
|
on tags (tag_registry_id, tag_manifest_id);
|
||||||
|
|
||||||
|
create table upstream_proxy_configs
|
||||||
|
(
|
||||||
|
upstream_proxy_config_id SERIAL primary key,
|
||||||
|
upstream_proxy_config_registry_id INTEGER not null
|
||||||
|
constraint fk_upstream_proxy_config_registry_id
|
||||||
|
references registries
|
||||||
|
on delete cascade,
|
||||||
|
upstream_proxy_config_source text,
|
||||||
|
upstream_proxy_config_url text,
|
||||||
|
upstream_proxy_config_auth_type text not null,
|
||||||
|
upstream_proxy_config_user_name text,
|
||||||
|
upstream_proxy_config_secret_identifier text,
|
||||||
|
upstream_proxy_config_secret_space_id INTEGER,
|
||||||
|
constraint fk_layers_secret_identifier_and_secret_space_id
|
||||||
|
foreign key (upstream_proxy_config_secret_identifier, upstream_proxy_config_secret_space_id)
|
||||||
|
references secrets(secret_uid, secret_space_id)
|
||||||
|
on delete cascade,
|
||||||
|
upstream_proxy_config_token text,
|
||||||
|
upstream_proxy_config_created_at BIGINT,
|
||||||
|
upstream_proxy_config_updated_at BIGINT,
|
||||||
|
upstream_proxy_config_created_by INTEGER,
|
||||||
|
upstream_proxy_config_updated_by INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_upstream_proxy_config_on_registry_id
|
||||||
|
on upstream_proxy_configs (upstream_proxy_config_registry_id);
|
||||||
|
|
||||||
|
create table cleanup_policies
|
||||||
|
(
|
||||||
|
cp_id SERIAL primary key,
|
||||||
|
cp_registry_id INTEGER not null
|
||||||
|
constraint fk_cleanup_policies_registry_id
|
||||||
|
references registries ON DELETE CASCADE,
|
||||||
|
cp_name text,
|
||||||
|
cp_expiry_time_ms BIGINT,
|
||||||
|
cp_created_at BIGINT not null,
|
||||||
|
cp_updated_at BIGINT not null,
|
||||||
|
cp_created_by INTEGER not null,
|
||||||
|
cp_updated_by INTEGER not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_cleanup_policies_on_registry_id
|
||||||
|
on cleanup_policies (cp_registry_id);
|
||||||
|
|
||||||
|
create table cleanup_policy_prefix_mappings
|
||||||
|
(
|
||||||
|
cpp_id SERIAL primary key,
|
||||||
|
cpp_cleanup_policy_id INTEGER not null
|
||||||
|
constraint fk_cleanup_policies_id
|
||||||
|
references cleanup_policies(cp_id) ON DELETE CASCADE,
|
||||||
|
cpp_prefix text not null,
|
||||||
|
cpp_prefix_type text not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_cleanup_policy_map_on_policy_id
|
||||||
|
on cleanup_policy_prefix_mappings (cpp_cleanup_policy_id);
|
||||||
|
|
||||||
|
insert into media_types (mt_media_type)
|
||||||
|
values ('application/vnd.docker.distribution.manifest.v1+json'),
|
||||||
|
('application/vnd.docker.distribution.manifest.v1+prettyjws'),
|
||||||
|
('application/vnd.docker.distribution.manifest.v2+json'),
|
||||||
|
('application/vnd.docker.distribution.manifest.list.v2+json'),
|
||||||
|
('application/vnd.docker.image.rootfs.diff.tar'),
|
||||||
|
('application/vnd.docker.image.rootfs.diff.tar.gzip'),
|
||||||
|
('application/vnd.docker.image.rootfs.foreign.diff.tar.gzip'),
|
||||||
|
('application/vnd.docker.container.image.v1+json'),
|
||||||
|
('application/vnd.docker.container.image.rootfs.diff+x-gtar'),
|
||||||
|
('application/vnd.docker.plugin.v1+json'),
|
||||||
|
('application/vnd.oci.image.layer.v1.tar'),
|
||||||
|
('application/vnd.oci.image.layer.v1.tar+gzip'),
|
||||||
|
('application/vnd.oci.image.layer.v1.tar+zstd'),
|
||||||
|
('application/vnd.oci.image.layer.nondistributable.v1.tar'),
|
||||||
|
('application/vnd.oci.image.layer.nondistributable.v1.tar+gzip'),
|
||||||
|
('application/vnd.oci.image.config.v1+json'),
|
||||||
|
('application/vnd.oci.image.manifest.v1+json'),
|
||||||
|
('application/vnd.oci.image.index.v1+json'),
|
||||||
|
('application/vnd.cncf.helm.config.v1+json'),
|
||||||
|
('application/tar+gzip'),
|
||||||
|
('application/octet-stream'),
|
||||||
|
('application/vnd.buildkit.cacheconfig.v0'),
|
||||||
|
('application/vnd.cncf.helm.chart.content.v1.tar+gzip'),
|
||||||
|
('application/vnd.cncf.helm.chart.provenance.v1.prov');
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE gc_blob_review_queue
|
||||||
|
(
|
||||||
|
blob_id INTEGER NOT NULL,
|
||||||
|
review_after BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM (NOW() + INTERVAL '1 day'))),
|
||||||
|
review_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
created_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()),
|
||||||
|
event text NOT NULL,
|
||||||
|
CONSTRAINT pk_gc_blob_review_queue primary key (blob_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX index_gc_blob_review_queue_on_review_after ON gc_blob_review_queue USING btree (review_after);
|
||||||
|
|
||||||
|
CREATE TABLE gc_review_after_defaults
|
||||||
|
(
|
||||||
|
event text NOT NULL,
|
||||||
|
value interval NOT NULL,
|
||||||
|
CONSTRAINT pk_gc_review_after_defaults PRIMARY KEY (event),
|
||||||
|
CONSTRAINT check_gc_review_after_defaults_event_length CHECK ((char_length(event) <= 255))
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO gc_review_after_defaults (event, value)
|
||||||
|
VALUES ('blob_upload', interval '1 day'),
|
||||||
|
('manifest_upload', interval '1 day'),
|
||||||
|
('manifest_delete', interval '1 day'),
|
||||||
|
('layer_delete', interval '1 day'),
|
||||||
|
('manifest_list_delete', interval '1 day'),
|
||||||
|
('tag_delete', interval '1 day'),
|
||||||
|
('tag_switch', interval '1 day')
|
||||||
|
ON CONFLICT (event)
|
||||||
|
DO NOTHING;
|
||||||
|
|
||||||
|
CREATE TABLE gc_manifest_review_queue
|
||||||
|
(
|
||||||
|
registry_id INTEGER NOT NULL,
|
||||||
|
manifest_id INTEGER NOT NULL,
|
||||||
|
review_after BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM (NOW() + INTERVAL '1 day'))),
|
||||||
|
review_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
created_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()),
|
||||||
|
event text NOT NULL,
|
||||||
|
CONSTRAINT pk_gc_manifest_review_queue PRIMARY KEY (registry_id, manifest_id),
|
||||||
|
CONSTRAINT fk_gc_manifest_review_queue_rp_id_mfst_id_mnfsts FOREIGN KEY (manifest_id) REFERENCES manifests (manifest_id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX index_gc_manifest_review_queue_on_review_after ON gc_manifest_review_queue USING btree (review_after);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_review_after(e text)
|
||||||
|
RETURNS BIGINT
|
||||||
|
VOLATILE
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
DECLARE
|
||||||
|
result timestamp WITH time zone;
|
||||||
|
BEGIN
|
||||||
|
SELECT (now() + value)
|
||||||
|
INTO result
|
||||||
|
FROM gc_review_after_defaults
|
||||||
|
WHERE event = e;
|
||||||
|
IF result IS NULL THEN
|
||||||
|
RETURN EXTRACT(EPOCH FROM (now() + interval '1 day'));
|
||||||
|
ELSE
|
||||||
|
RETURN EXTRACT(EPOCH FROM result);
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_track_blob_uploads()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO gc_blob_review_queue (blob_id, review_after, event)
|
||||||
|
VALUES (NEW.blob_id, gc_review_after('blob_upload'), 'blob_upload')
|
||||||
|
ON CONFLICT (blob_id)
|
||||||
|
DO UPDATE SET review_after = gc_review_after('blob_upload'),
|
||||||
|
event = 'blob_upload';
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER gc_track_blob_uploads_trigger
|
||||||
|
AFTER INSERT
|
||||||
|
ON blobs
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE public.gc_track_blob_uploads();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_track_manifest_uploads()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO gc_manifest_review_queue (registry_id, manifest_id, review_after, event)
|
||||||
|
VALUES (NEW.manifest_registry_id, NEW.manifest_id, gc_review_after('manifest_upload'), 'manifest_upload');
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER gc_track_manifest_uploads_trigger
|
||||||
|
AFTER INSERT
|
||||||
|
ON manifests
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE gc_track_manifest_uploads();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_track_deleted_manifests()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF OLD.manifest_configuration_blob_id IS NOT NULL THEN -- not all manifests have a configuration
|
||||||
|
INSERT INTO gc_blob_review_queue (blob_id, review_after, event)
|
||||||
|
VALUES (OLD.manifest_configuration_blob_id, gc_review_after('manifest_delete'), 'manifest_delete')
|
||||||
|
ON CONFLICT (blob_id)
|
||||||
|
DO UPDATE SET
|
||||||
|
review_after = gc_review_after('manifest_delete'),
|
||||||
|
event = 'manifest_delete';
|
||||||
|
END IF;
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_track_deleted_layers()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF (TG_LEVEL = 'STATEMENT') THEN
|
||||||
|
INSERT INTO gc_blob_review_queue (blob_id, review_after, event)
|
||||||
|
SELECT deleted_rows.layer_blob_id,
|
||||||
|
gc_review_after('layer_delete'),
|
||||||
|
'layer_delete'
|
||||||
|
FROM old_table deleted_rows
|
||||||
|
JOIN
|
||||||
|
blobs b ON deleted_rows.layer_blob_id = b.blob_id
|
||||||
|
ORDER BY deleted_rows.layer_blob_id ASC
|
||||||
|
ON CONFLICT (blob_id)
|
||||||
|
DO UPDATE SET review_after = gc_review_after('layer_delete'),
|
||||||
|
event = 'layer_delete';
|
||||||
|
ELSIF (TG_LEVEL = 'ROW') THEN
|
||||||
|
INSERT INTO gc_blob_review_queue (blob_id, review_after, event)
|
||||||
|
VALUES (OLD.blob_id, gc_review_after('layer_delete'), 'layer_delete')
|
||||||
|
ON CONFLICT (blob_id)
|
||||||
|
DO UPDATE SET review_after = gc_review_after('layer_delete'),
|
||||||
|
event = 'layer_delete';
|
||||||
|
END IF;
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER gc_track_deleted_manifests_trigger
|
||||||
|
AFTER DELETE
|
||||||
|
ON manifests
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE gc_track_deleted_manifests();
|
||||||
|
|
||||||
|
CREATE TRIGGER gc_track_deleted_layers_trigger
|
||||||
|
AFTER DELETE
|
||||||
|
ON layers
|
||||||
|
REFERENCING OLD TABLE AS old_table
|
||||||
|
FOR EACH STATEMENT
|
||||||
|
EXECUTE FUNCTION gc_track_deleted_layers();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_track_deleted_manifest_lists()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO gc_manifest_review_queue (registry_id, manifest_id, review_after, event)
|
||||||
|
VALUES (OLD.manifest_ref_registry_id, OLD.manifest_ref_child_id, gc_review_after('manifest_list_delete'), 'manifest_list_delete')
|
||||||
|
ON CONFLICT (registry_id, manifest_id)
|
||||||
|
DO UPDATE SET review_after = gc_review_after('manifest_list_delete'),
|
||||||
|
event = 'manifest_list_delete';
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER gc_track_deleted_manifest_lists_trigger
|
||||||
|
AFTER DELETE
|
||||||
|
ON manifest_references
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE gc_track_deleted_manifest_lists();
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_track_deleted_tags()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1
|
||||||
|
FROM manifests
|
||||||
|
WHERE manifest_registry_id = OLD.tag_registry_id
|
||||||
|
AND manifest_id = OLD.tag_registry_id) THEN
|
||||||
|
INSERT INTO gc_manifest_review_queue (registry_id, manifest_id, review_after, event)
|
||||||
|
VALUES (OLD.tag_registry_id, OLD.tag_manifest_id, gc_review_after('tag_delete'), 'tag_delete')
|
||||||
|
ON CONFLICT (registry_id, manifest_id)
|
||||||
|
DO UPDATE SET review_after = gc_review_after('tag_delete'),
|
||||||
|
event = 'tag_delete';
|
||||||
|
END IF;
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER gc_track_deleted_tag_trigger
|
||||||
|
AFTER DELETE
|
||||||
|
ON tags
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE gc_track_deleted_tags();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION gc_track_switched_tags()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO gc_manifest_review_queue (registry_id, manifest_id, review_after, event)
|
||||||
|
VALUES (OLD.tag_registry_id, OLD.tag_manifest_id, gc_review_after('tag_switch'), 'tag_switch')
|
||||||
|
ON CONFLICT (registry_id, manifest_id)
|
||||||
|
DO UPDATE SET review_after = gc_review_after('tag_switch'),
|
||||||
|
event = 'tag_switch';
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER gc_track_switched_tag_trigger
|
||||||
|
AFTER UPDATE OF tag_manifest_id
|
||||||
|
ON tags
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE gc_track_switched_tags();
|
@ -0,0 +1,13 @@
|
|||||||
|
DROP TABLE registries;
|
||||||
|
DROP TABLE media_types;
|
||||||
|
DROP TABLE blobs;
|
||||||
|
DROP TABLE registry_blobs;
|
||||||
|
DROP TABLE manifests;
|
||||||
|
DROP TABLE manifest_references;
|
||||||
|
DROP TABLE layers;
|
||||||
|
DROP TABLE artifacts;
|
||||||
|
DROP TABLE artifact_stats;
|
||||||
|
DROP TABLE tags;
|
||||||
|
DROP TABLE upstream_proxy_configs;
|
||||||
|
DROP TABLE cleanup_policies;
|
||||||
|
DROP TABLE cleanup_policy_prefix_mappings;
|
330
app/store/database/migrate/sqlite/0065_create_ar_tables.up.sql
Normal file
330
app/store/database/migrate/sqlite/0065_create_ar_tables.up.sql
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
create table registries
|
||||||
|
(
|
||||||
|
registry_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
registry_name text not null
|
||||||
|
constraint registry_name_len_check
|
||||||
|
check (length(registry_name) <= 255),
|
||||||
|
registry_root_parent_id INTEGER not null,
|
||||||
|
registry_parent_id INTEGER not null,
|
||||||
|
registry_description text,
|
||||||
|
registry_type text not null,
|
||||||
|
registry_package_type text not null,
|
||||||
|
registry_upstream_proxies text,
|
||||||
|
registry_allowed_pattern text,
|
||||||
|
registry_blocked_pattern text,
|
||||||
|
registry_labels text,
|
||||||
|
registry_created_at INTEGER not null,
|
||||||
|
registry_updated_at INTEGER not null,
|
||||||
|
registry_created_by INTEGER not null,
|
||||||
|
registry_updated_by INTEGER not null,
|
||||||
|
constraint unique_registries
|
||||||
|
unique (registry_root_parent_id, registry_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table media_types
|
||||||
|
(
|
||||||
|
mt_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
mt_media_type text not null
|
||||||
|
constraint unique_media_types_type
|
||||||
|
unique,
|
||||||
|
mt_created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table blobs
|
||||||
|
(
|
||||||
|
blob_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
blob_root_parent_id INTEGER not null,
|
||||||
|
blob_digest bytea not null,
|
||||||
|
blob_media_type_id INTEGER not null
|
||||||
|
constraint fk_blobs_media_type_id_media_types
|
||||||
|
references media_types(mt_id),
|
||||||
|
blob_size INTEGER not null,
|
||||||
|
blob_created_at INTEGER not null,
|
||||||
|
blob_created_by INTEGER not null,
|
||||||
|
constraint unique_digest_root_parent_id unique (blob_digest, blob_root_parent_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_blobs_on_media_type_id
|
||||||
|
on blobs (blob_media_type_id);
|
||||||
|
|
||||||
|
create table registry_blobs
|
||||||
|
(
|
||||||
|
rblob_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
rblob_registry_id INTEGER not null
|
||||||
|
constraint fk_registry_blobs_rpstry_id_registries
|
||||||
|
references registries(registry_id)
|
||||||
|
on delete cascade,
|
||||||
|
rblob_blob_id INTEGER not null
|
||||||
|
constraint fk_registry_blobs_blob_id_blobs
|
||||||
|
references blobs(blob_id)
|
||||||
|
on delete cascade,
|
||||||
|
rblob_image_name text
|
||||||
|
constraint registry_blobs_image_len_check
|
||||||
|
check (length(rblob_image_name) <= 255),
|
||||||
|
rblob_created_at INTEGER not null,
|
||||||
|
rblob_updated_at INTEGER not null,
|
||||||
|
rblob_created_by INTEGER not null,
|
||||||
|
rblob_updated_by INTEGER not null,
|
||||||
|
|
||||||
|
constraint unique_registry_blobs_registry_id_blob_id_image
|
||||||
|
unique (rblob_registry_id, rblob_blob_id, rblob_image_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_registry_blobs_on_reg_id
|
||||||
|
on registry_blobs (rblob_registry_id);
|
||||||
|
|
||||||
|
create index index_registry_blobs_on_reg_blob_id
|
||||||
|
on registry_blobs (rblob_registry_id, rblob_blob_id);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
create table manifests
|
||||||
|
(
|
||||||
|
manifest_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
manifest_registry_id INTEGER not null
|
||||||
|
constraint fk_manifests_registry_id_registries
|
||||||
|
references registries(registry_id)
|
||||||
|
on delete cascade,
|
||||||
|
manifest_schema_version smallint not null,
|
||||||
|
manifest_media_type_id INTEGER not null
|
||||||
|
constraint fk_manifests_media_type_id_media_types
|
||||||
|
references media_types(mt_id),
|
||||||
|
manifest_artifact_media_type text,
|
||||||
|
manifest_total_size INTEGER not null,
|
||||||
|
manifest_configuration_media_type text,
|
||||||
|
manifest_configuration_payload bytea,
|
||||||
|
manifest_configuration_blob_id INTEGER
|
||||||
|
constraint fk_manifests_configuration_blob_id_blobs
|
||||||
|
references blobs(blob_id),
|
||||||
|
manifest_configuration_digest bytea,
|
||||||
|
manifest_digest bytea not null,
|
||||||
|
manifest_payload bytea not null,
|
||||||
|
manifest_non_conformant boolean default false,
|
||||||
|
manifest_non_distributable_layers boolean default false,
|
||||||
|
manifest_subject_id INTEGER,
|
||||||
|
manifest_subject_digest bytea,
|
||||||
|
manifest_annotations bytea,
|
||||||
|
manifest_image_name text not null
|
||||||
|
constraint manifests_img_name_len_check
|
||||||
|
check (length(manifest_image_name) <= 255),
|
||||||
|
manifest_created_at INTEGER not null,
|
||||||
|
manifest_created_by INTEGER not null,
|
||||||
|
manifest_updated_at INTEGER not null,
|
||||||
|
manifest_updated_by INTEGER not null,
|
||||||
|
constraint unique_manifests_registry_id_image_name_and_digest
|
||||||
|
unique (manifest_registry_id, manifest_image_name, manifest_digest),
|
||||||
|
constraint unique_manifests_registry_id_id_cfg_blob_id
|
||||||
|
unique (manifest_registry_id, manifest_id, manifest_configuration_blob_id),
|
||||||
|
constraint fk_manifests_subject_id_manifests
|
||||||
|
foreign key (manifest_subject_id) references manifests(manifest_id)
|
||||||
|
on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_manifests_on_media_type_id
|
||||||
|
on manifests (manifest_media_type_id);
|
||||||
|
|
||||||
|
create index index_manifests_on_configuration_blob_id
|
||||||
|
on manifests (manifest_configuration_blob_id);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
create table manifest_references
|
||||||
|
(
|
||||||
|
manifest_ref_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
manifest_ref_registry_id INTEGER not null,
|
||||||
|
manifest_ref_parent_id INTEGER not null,
|
||||||
|
manifest_ref_child_id INTEGER not null,
|
||||||
|
manifest_ref_created_at INTEGER not null,
|
||||||
|
manifest_ref_updated_at INTEGER not null,
|
||||||
|
manifest_ref_created_by INTEGER not null,
|
||||||
|
manifest_ref_updated_by INTEGER not null,
|
||||||
|
constraint unique_manifest_references_prt_id_chd_id
|
||||||
|
unique (manifest_ref_registry_id, manifest_ref_parent_id, manifest_ref_child_id),
|
||||||
|
constraint fk_manifest_ref_parent_id_manifests_manifest_id
|
||||||
|
foreign key (manifest_ref_parent_id) references manifests(manifest_id)
|
||||||
|
on delete cascade,
|
||||||
|
constraint fk_manifest_ref_child_id_manifests_manifest_id
|
||||||
|
foreign key (manifest_ref_child_id) references manifests(manifest_id),
|
||||||
|
constraint check_manifest_references_parent_id_and_child_id_differ
|
||||||
|
check (manifest_ref_parent_id <> manifest_ref_child_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_manifest_references_on_rpstry_id_child_id
|
||||||
|
on manifest_references (manifest_ref_registry_id, manifest_ref_child_id);
|
||||||
|
|
||||||
|
create table layers
|
||||||
|
(
|
||||||
|
layer_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
layer_registry_id INTEGER not null,
|
||||||
|
layer_manifest_id INTEGER not null,
|
||||||
|
layer_media_type_id INTEGER not null
|
||||||
|
constraint fk_layer_media_type_id_media_types
|
||||||
|
references media_types(mt_id),
|
||||||
|
layer_blob_id INTEGER not null
|
||||||
|
constraint fk_layer_blob_id_blobs
|
||||||
|
references blobs(blob_id),
|
||||||
|
layer_size INTEGER not null,
|
||||||
|
layer_created_at INTEGER not null,
|
||||||
|
layer_updated_at INTEGER not null,
|
||||||
|
layer_created_by INTEGER not null,
|
||||||
|
layer_updated_by INTEGER not null,
|
||||||
|
constraint unique_layer_rpstry_id_and_mnfst_id_and_blob_id
|
||||||
|
unique (layer_registry_id, layer_manifest_id, layer_blob_id),
|
||||||
|
constraint unique_layer_rpstry_id_and_id_and_blob_id
|
||||||
|
unique (layer_registry_id, layer_id, layer_blob_id),
|
||||||
|
constraint fk_layer_manifest_id_and_manifests_manifest_id
|
||||||
|
foreign key (layer_manifest_id) references manifests(manifest_id)
|
||||||
|
on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_layer_on_media_type_id
|
||||||
|
on layers (layer_media_type_id);
|
||||||
|
|
||||||
|
create index index_layer_on_blob_id
|
||||||
|
on layers (layer_blob_id);
|
||||||
|
|
||||||
|
create table artifacts
|
||||||
|
(
|
||||||
|
artifact_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
artifact_name text not null,
|
||||||
|
artifact_registry_id INTEGER not null
|
||||||
|
constraint fk_registries_registry_id
|
||||||
|
references registries(registry_id)
|
||||||
|
on delete cascade,
|
||||||
|
artifact_labels text,
|
||||||
|
artifact_enabled boolean default false,
|
||||||
|
artifact_created_at INTEGER,
|
||||||
|
artifact_updated_at INTEGER,
|
||||||
|
artifact_created_by INTEGER,
|
||||||
|
artifact_updated_by INTEGER,
|
||||||
|
constraint unique_artifact_registry_id_and_name unique (artifact_registry_id, artifact_name),
|
||||||
|
constraint check_artifact_name_length check ((length(artifact_name) <= 255))
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_artifact_on_registry_id ON artifacts (artifact_registry_id);
|
||||||
|
|
||||||
|
|
||||||
|
create table artifact_stats
|
||||||
|
(
|
||||||
|
artifact_stat_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
artifact_stat_artifact_id INTEGER not null
|
||||||
|
constraint fk_artifacts_artifact_id
|
||||||
|
references artifacts(artifact_id),
|
||||||
|
artifact_stat_date INTEGER,
|
||||||
|
artifact_stat_download_count INTEGER,
|
||||||
|
artifact_stat_upload_bytes INTEGER,
|
||||||
|
artifact_stat_download_bytes INTEGER,
|
||||||
|
artifact_stat_created_at INTEGER not null,
|
||||||
|
artifact_stat_updated_at INTEGER not null,
|
||||||
|
artifact_stat_created_by INTEGER not null,
|
||||||
|
artifact_stat_updated_by INTEGER not null,
|
||||||
|
constraint unique_artifact_stats_artifact_id_and_date unique (artifact_stat_artifact_id, artifact_stat_date)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table tags
|
||||||
|
(
|
||||||
|
tag_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
tag_name text not null
|
||||||
|
constraint tag_name_len_check
|
||||||
|
check (length(tag_name) <= 128),
|
||||||
|
tag_image_name text not null
|
||||||
|
constraint tag_img_name_len_check
|
||||||
|
check (length(tag_image_name) <= 255),
|
||||||
|
tag_registry_id INTEGER not null,
|
||||||
|
tag_manifest_id INTEGER not null,
|
||||||
|
tag_created_at INTEGER,
|
||||||
|
tag_updated_at INTEGER,
|
||||||
|
tag_created_by INTEGER,
|
||||||
|
tag_updated_by INTEGER,
|
||||||
|
constraint fk_tag_manifest_id_and_manifests_manifest_id FOREIGN KEY
|
||||||
|
(tag_manifest_id) REFERENCES manifests (manifest_id) ON DELETE CASCADE,
|
||||||
|
constraint unique_tag_registry_id_and_name_and_image_name
|
||||||
|
unique (tag_registry_id, tag_name, tag_image_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_tag_on_rpository_id_and_manifest_id
|
||||||
|
on tags (tag_registry_id, tag_manifest_id);
|
||||||
|
|
||||||
|
create table upstream_proxy_configs
|
||||||
|
(
|
||||||
|
upstream_proxy_config_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
upstream_proxy_config_registry_id INTEGER not null
|
||||||
|
constraint fk_upstream_proxy_config_registry_id
|
||||||
|
references registries(registry_id)
|
||||||
|
on delete cascade,
|
||||||
|
upstream_proxy_config_source text,
|
||||||
|
upstream_proxy_config_url text,
|
||||||
|
upstream_proxy_config_auth_type text not null,
|
||||||
|
upstream_proxy_config_user_name text,
|
||||||
|
upstream_proxy_config_secret_identifier text,
|
||||||
|
upstream_proxy_config_secret_space_id int,
|
||||||
|
upstream_proxy_config_token text,
|
||||||
|
upstream_proxy_config_created_at INTEGER,
|
||||||
|
upstream_proxy_config_updated_at INTEGER,
|
||||||
|
upstream_proxy_config_created_by INTEGER,
|
||||||
|
upstream_proxy_config_updated_by INTEGER,
|
||||||
|
constraint fk_layers_secret_identifier_and_secret_space_id FOREIGN KEY
|
||||||
|
(upstream_proxy_config_secret_identifier, upstream_proxy_config_secret_space_id) REFERENCES secrets(secret_uid, secret_space_id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_upstream_proxy_config_on_registry_id
|
||||||
|
on upstream_proxy_configs (upstream_proxy_config_registry_id);
|
||||||
|
|
||||||
|
create table cleanup_policies
|
||||||
|
(
|
||||||
|
cp_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
cp_registry_id INTEGER not null
|
||||||
|
constraint fk_cleanup_policies_registry_id
|
||||||
|
references registries(registry_id) ON DELETE CASCADE,
|
||||||
|
cp_name text,
|
||||||
|
cp_expiry_time_ms INTEGER,
|
||||||
|
cp_created_at INTEGER not null,
|
||||||
|
cp_updated_at INTEGER not null,
|
||||||
|
cp_created_by INTEGER not null,
|
||||||
|
cp_updated_by INTEGER not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_cleanup_policies_on_registry_id
|
||||||
|
on cleanup_policies (cp_registry_id);
|
||||||
|
|
||||||
|
create table cleanup_policy_prefix_mappings
|
||||||
|
(
|
||||||
|
cpp_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
cpp_cleanup_policy_id INTEGER not null
|
||||||
|
constraint fk_cleanup_policy_prefix_registry_id
|
||||||
|
references cleanup_policies(cp_id) ON DELETE CASCADE,
|
||||||
|
cpp_prefix text not null,
|
||||||
|
cpp_prefix_type text not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create index index_cleanup_policy_map_on_policy_id
|
||||||
|
on cleanup_policy_prefix_mappings (cpp_cleanup_policy_id);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
insert into media_types (mt_media_type)
|
||||||
|
values ('application/vnd.docker.distribution.manifest.v1+json'),
|
||||||
|
('application/vnd.docker.distribution.manifest.v1+prettyjws'),
|
||||||
|
('application/vnd.docker.distribution.manifest.v2+json'),
|
||||||
|
('application/vnd.docker.distribution.manifest.list.v2+json'),
|
||||||
|
('application/vnd.docker.image.rootfs.diff.tar'),
|
||||||
|
('application/vnd.docker.image.rootfs.diff.tar.gzip'),
|
||||||
|
('application/vnd.docker.image.rootfs.foreign.diff.tar.gzip'),
|
||||||
|
('application/vnd.docker.container.image.v1+json'),
|
||||||
|
('application/vnd.docker.container.image.rootfs.diff+x-gtar'),
|
||||||
|
('application/vnd.docker.plugin.v1+json'),
|
||||||
|
('application/vnd.oci.image.layer.v1.tar'),
|
||||||
|
('application/vnd.oci.image.layer.v1.tar+gzip'),
|
||||||
|
('application/vnd.oci.image.layer.v1.tar+zstd'),
|
||||||
|
('application/vnd.oci.image.layer.nondistributable.v1.tar'),
|
||||||
|
('application/vnd.oci.image.layer.nondistributable.v1.tar+gzip'),
|
||||||
|
('application/vnd.oci.image.config.v1+json'),
|
||||||
|
('application/vnd.oci.image.manifest.v1+json'),
|
||||||
|
('application/vnd.oci.image.index.v1+json'),
|
||||||
|
('application/vnd.cncf.helm.config.v1+json'),
|
||||||
|
('application/tar+gzip'),
|
||||||
|
('application/octet-stream'),
|
||||||
|
('application/vnd.buildkit.cacheconfig.v0'),
|
||||||
|
('application/vnd.cncf.helm.chart.content.v1.tar+gzip'),
|
||||||
|
('application/vnd.cncf.helm.chart.provenance.v1.prov');
|
@ -223,14 +223,16 @@ func ProvideTokenStore(db *sqlx.DB) store.TokenStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ProvidePullReqStore provides a pull request store.
|
// ProvidePullReqStore provides a pull request store.
|
||||||
func ProvidePullReqStore(db *sqlx.DB,
|
func ProvidePullReqStore(
|
||||||
|
db *sqlx.DB,
|
||||||
principalInfoCache store.PrincipalInfoCache,
|
principalInfoCache store.PrincipalInfoCache,
|
||||||
) store.PullReqStore {
|
) store.PullReqStore {
|
||||||
return NewPullReqStore(db, principalInfoCache)
|
return NewPullReqStore(db, principalInfoCache)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvidePullReqActivityStore provides a pull request activity store.
|
// ProvidePullReqActivityStore provides a pull request activity store.
|
||||||
func ProvidePullReqActivityStore(db *sqlx.DB,
|
func ProvidePullReqActivityStore(
|
||||||
|
db *sqlx.DB,
|
||||||
principalInfoCache store.PrincipalInfoCache,
|
principalInfoCache store.PrincipalInfoCache,
|
||||||
) store.PullReqActivityStore {
|
) store.PullReqActivityStore {
|
||||||
return NewPullReqActivityStore(db, principalInfoCache)
|
return NewPullReqActivityStore(db, principalInfoCache)
|
||||||
@ -247,7 +249,8 @@ func ProvidePullReqReviewStore(db *sqlx.DB) store.PullReqReviewStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ProvidePullReqReviewerStore provides a pull request reviewer store.
|
// ProvidePullReqReviewerStore provides a pull request reviewer store.
|
||||||
func ProvidePullReqReviewerStore(db *sqlx.DB,
|
func ProvidePullReqReviewerStore(
|
||||||
|
db *sqlx.DB,
|
||||||
principalInfoCache store.PrincipalInfoCache,
|
principalInfoCache store.PrincipalInfoCache,
|
||||||
) store.PullReqReviewerStore {
|
) store.PullReqReviewerStore {
|
||||||
return NewPullReqReviewerStore(db, principalInfoCache)
|
return NewPullReqReviewerStore(db, principalInfoCache)
|
||||||
@ -269,7 +272,8 @@ func ProvideWebhookExecutionStore(db *sqlx.DB) store.WebhookExecutionStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ProvideCheckStore provides a status check result store.
|
// ProvideCheckStore provides a status check result store.
|
||||||
func ProvideCheckStore(db *sqlx.DB,
|
func ProvideCheckStore(
|
||||||
|
db *sqlx.DB,
|
||||||
principalInfoCache store.PrincipalInfoCache,
|
principalInfoCache store.PrincipalInfoCache,
|
||||||
) store.CheckStore {
|
) store.CheckStore {
|
||||||
return NewCheckStore(db, principalInfoCache)
|
return NewCheckStore(db, principalInfoCache)
|
||||||
|
@ -30,9 +30,22 @@ import (
|
|||||||
const (
|
const (
|
||||||
// userSessionTokenLifeTime is the duration a login / register token is valid.
|
// userSessionTokenLifeTime is the duration a login / register token is valid.
|
||||||
// NOTE: Users can list / delete session tokens via rest API if they want to cleanup earlier.
|
// NOTE: Users can list / delete session tokens via rest API if they want to cleanup earlier.
|
||||||
userSessionTokenLifeTime time.Duration = 30 * 24 * time.Hour // 30 days.
|
userSessionTokenLifeTime time.Duration = 30 * 24 * time.Hour // 30 days.
|
||||||
|
sessionTokenWithAccessPermissionsLifeTime time.Duration = 24 * time.Hour // 24 hours.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func CreateUserWithAccessPermissions(
|
||||||
|
user *types.User,
|
||||||
|
accessPermissions *jwt.SubClaimsAccessPermissions,
|
||||||
|
) (string, error) {
|
||||||
|
principal := user.ToPrincipal()
|
||||||
|
return createWithAccessPermissions(
|
||||||
|
principal,
|
||||||
|
ptr.Duration(sessionTokenWithAccessPermissionsLifeTime),
|
||||||
|
accessPermissions,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateUserSession(
|
func CreateUserSession(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
tokenStore store.TokenStore,
|
tokenStore store.TokenStore,
|
||||||
@ -128,3 +141,18 @@ func create(
|
|||||||
|
|
||||||
return &token, jwtToken, nil
|
return &token, jwtToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createWithAccessPermissions(
|
||||||
|
createdFor *types.Principal,
|
||||||
|
lifetime *time.Duration,
|
||||||
|
accessPermissions *jwt.SubClaimsAccessPermissions,
|
||||||
|
) (string, error) {
|
||||||
|
jwtToken, err := jwt.GenerateForTokenWithAccessPermissions(
|
||||||
|
createdFor.ID, lifetime, createdFor.Salt, accessPermissions,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create jwt token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jwtToken, nil
|
||||||
|
}
|
||||||
|
@ -74,6 +74,9 @@ type Provider interface {
|
|||||||
|
|
||||||
// GetAPIProto returns the proto for the API hostname
|
// GetAPIProto returns the proto for the API hostname
|
||||||
GetAPIProto(ctx context.Context) string
|
GetAPIProto(ctx context.Context) string
|
||||||
|
|
||||||
|
// RegistryURL returns the url for oci token endpoint
|
||||||
|
RegistryURL() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider provides the URLs of the gitness system.
|
// Provider provides the URLs of the gitness system.
|
||||||
@ -99,6 +102,9 @@ type provider struct {
|
|||||||
|
|
||||||
// uiURL stores the raw URL to the ui endpoints.
|
// uiURL stores the raw URL to the ui endpoints.
|
||||||
uiURL *url.URL
|
uiURL *url.URL
|
||||||
|
|
||||||
|
// registryURL stores the raw URL to the registry endpoints.
|
||||||
|
registryURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProvider(
|
func NewProvider(
|
||||||
@ -110,6 +116,7 @@ func NewProvider(
|
|||||||
sshDefaultUser string,
|
sshDefaultUser string,
|
||||||
sshEnabled bool,
|
sshEnabled bool,
|
||||||
uiURLRaw string,
|
uiURLRaw string,
|
||||||
|
registryURLRaw string,
|
||||||
) (Provider, error) {
|
) (Provider, error) {
|
||||||
// remove trailing '/' to make usage easier
|
// remove trailing '/' to make usage easier
|
||||||
internalURLRaw = strings.TrimRight(internalURLRaw, "/")
|
internalURLRaw = strings.TrimRight(internalURLRaw, "/")
|
||||||
@ -118,6 +125,7 @@ func NewProvider(
|
|||||||
gitURLRaw = strings.TrimRight(gitURLRaw, "/")
|
gitURLRaw = strings.TrimRight(gitURLRaw, "/")
|
||||||
gitSSHURLRaw = strings.TrimRight(gitSSHURLRaw, "/")
|
gitSSHURLRaw = strings.TrimRight(gitSSHURLRaw, "/")
|
||||||
uiURLRaw = strings.TrimRight(uiURLRaw, "/")
|
uiURLRaw = strings.TrimRight(uiURLRaw, "/")
|
||||||
|
registryURLRaw = strings.TrimRight(registryURLRaw, "/")
|
||||||
|
|
||||||
internalURL, err := url.Parse(internalURLRaw)
|
internalURL, err := url.Parse(internalURLRaw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -149,6 +157,11 @@ func NewProvider(
|
|||||||
return nil, fmt.Errorf("provided uiURLRaw '%s' is invalid: %w", uiURLRaw, err)
|
return nil, fmt.Errorf("provided uiURLRaw '%s' is invalid: %w", uiURLRaw, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registryURL, err := url.Parse(registryURLRaw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("provided registryURLRaw '%s' is invalid: %w", registryURLRaw, err)
|
||||||
|
}
|
||||||
|
|
||||||
return &provider{
|
return &provider{
|
||||||
internalURL: internalURL,
|
internalURL: internalURL,
|
||||||
containerURL: containerURL,
|
containerURL: containerURL,
|
||||||
@ -158,6 +171,7 @@ func NewProvider(
|
|||||||
SSHDefaultUser: sshDefaultUser,
|
SSHDefaultUser: sshDefaultUser,
|
||||||
SSHEnabled: sshEnabled,
|
SSHEnabled: sshEnabled,
|
||||||
uiURL: uiURL,
|
uiURL: uiURL,
|
||||||
|
registryURL: registryURL,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,8 +205,10 @@ func (p *provider) GenerateGITCloneSSHURL(_ context.Context, repoPath string) st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *provider) GenerateUIBuildURL(_ context.Context, repoPath, pipelineIdentifier string, seqNumber int64) string {
|
func (p *provider) GenerateUIBuildURL(_ context.Context, repoPath, pipelineIdentifier string, seqNumber int64) string {
|
||||||
return p.uiURL.JoinPath(repoPath, "pipelines",
|
return p.uiURL.JoinPath(
|
||||||
pipelineIdentifier, "execution", strconv.Itoa(int(seqNumber))).String()
|
repoPath, "pipelines",
|
||||||
|
pipelineIdentifier, "execution", strconv.Itoa(int(seqNumber)),
|
||||||
|
).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *provider) GenerateUIRepoURL(_ context.Context, repoPath string) string {
|
func (p *provider) GenerateUIRepoURL(_ context.Context, repoPath string) string {
|
||||||
@ -219,6 +235,10 @@ func (p *provider) GetAPIProto(context.Context) string {
|
|||||||
return p.apiURL.Scheme
|
return p.apiURL.Scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *provider) RegistryURL() string {
|
||||||
|
return p.registryURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
func BuildGITCloneSSHURL(user string, sshURL *url.URL, repoPath string) string {
|
func BuildGITCloneSSHURL(user string, sshURL *url.URL, repoPath string) string {
|
||||||
repoPath = path.Clean(repoPath)
|
repoPath = path.Clean(repoPath)
|
||||||
if !strings.HasSuffix(repoPath, GITSuffix) {
|
if !strings.HasSuffix(repoPath, GITSuffix) {
|
||||||
|
@ -33,5 +33,6 @@ func ProvideURLProvider(config *types.Config) (Provider, error) {
|
|||||||
config.SSH.DefaultUser,
|
config.SSH.DefaultUser,
|
||||||
config.SSH.Enable,
|
config.SSH.Enable,
|
||||||
config.URL.UI,
|
config.URL.UI,
|
||||||
|
config.URL.Registry,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -54,17 +54,22 @@ func (a Action) Validate() error {
|
|||||||
type ResourceType string
|
type ResourceType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ResourceTypeRepository ResourceType = "repository"
|
ResourceTypeRepository ResourceType = "repository"
|
||||||
ResourceTypeBranchRule ResourceType = "branch_rule"
|
ResourceTypeBranchRule ResourceType = "branch_rule"
|
||||||
ResourceTypeRepositorySettings ResourceType = "repository_settings"
|
ResourceTypeRepositorySettings ResourceType = "repository_settings"
|
||||||
|
ResourceTypeRegistry ResourceType = "registry"
|
||||||
|
ResourceTypeRegistryUpstreamProxy ResourceType = "registry_upstream_proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a ResourceType) Validate() error {
|
func (a ResourceType) Validate() error {
|
||||||
switch a {
|
switch a {
|
||||||
case ResourceTypeRepository,
|
case ResourceTypeRepository,
|
||||||
ResourceTypeBranchRule,
|
ResourceTypeBranchRule,
|
||||||
ResourceTypeRepositorySettings:
|
ResourceTypeRepositorySettings,
|
||||||
|
ResourceTypeRegistry,
|
||||||
|
ResourceTypeRegistryUpstreamProxy:
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ErrResourceTypeUndefined
|
return ErrResourceTypeUndefined
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,12 @@
|
|||||||
|
|
||||||
package audit
|
package audit
|
||||||
|
|
||||||
import "github.com/harness/gitness/types"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
registrytypes "github.com/harness/gitness/registry/types"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
)
|
||||||
|
|
||||||
// RepositoryObject is the object used for emitting repository related audits.
|
// RepositoryObject is the object used for emitting repository related audits.
|
||||||
// TODO: ensure audit only takes audit related objects?
|
// TODO: ensure audit only takes audit related objects?
|
||||||
@ -22,3 +27,18 @@ type RepositoryObject struct {
|
|||||||
types.Repository
|
types.Repository
|
||||||
IsPublic bool `yaml:"is_public"`
|
IsPublic bool `yaml:"is_public"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegistryObject struct {
|
||||||
|
registrytypes.Registry
|
||||||
|
}
|
||||||
|
type RegistryUpstreamProxyConfigObject struct {
|
||||||
|
ID int64
|
||||||
|
RegistryID int64
|
||||||
|
Source string
|
||||||
|
URL string
|
||||||
|
AuthType string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
CreatedBy int64
|
||||||
|
UpdatedBy int64
|
||||||
|
}
|
||||||
|
@ -34,6 +34,7 @@ func (c *commandCurrent) run(*kingpin.ParseContext) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
db, err := getDB(ctx, c.envfile)
|
db, err := getDB(ctx, c.envfile)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -194,6 +194,9 @@ func backfillURLs(config *types.Config) error {
|
|||||||
if config.URL.UI == "" {
|
if config.URL.UI == "" {
|
||||||
config.URL.UI = baseURL.String()
|
config.URL.UI = baseURL.String()
|
||||||
}
|
}
|
||||||
|
if config.URL.Registry == "" {
|
||||||
|
config.URL.Registry = baseURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -238,22 +241,26 @@ func getSanitizedMachineName() (string, error) {
|
|||||||
norm.NFD,
|
norm.NFD,
|
||||||
runes.ReplaceIllFormed(),
|
runes.ReplaceIllFormed(),
|
||||||
runes.Remove(runes.In(unicode.Mn)),
|
runes.Remove(runes.In(unicode.Mn)),
|
||||||
runes.Map(func(r rune) rune {
|
runes.Map(
|
||||||
switch {
|
func(r rune) rune {
|
||||||
case 'A' <= r && r <= 'Z':
|
switch {
|
||||||
return r + 32
|
case 'A' <= r && r <= 'Z':
|
||||||
case 'a' <= r && r <= 'z':
|
return r + 32
|
||||||
return r
|
case 'a' <= r && r <= 'z':
|
||||||
case '0' <= r && r <= '9':
|
return r
|
||||||
return r
|
case '0' <= r && r <= '9':
|
||||||
case r == '-', r == '.':
|
return r
|
||||||
return r
|
case r == '-', r == '.':
|
||||||
default:
|
return r
|
||||||
return '_'
|
default:
|
||||||
}
|
return '_'
|
||||||
}),
|
}
|
||||||
norm.NFC),
|
},
|
||||||
hostName)
|
),
|
||||||
|
norm.NFC,
|
||||||
|
),
|
||||||
|
hostName,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
// Copyright 2021 Harness Inc. All rights reserved.
|
// Copyright 2023 Harness, Inc.
|
||||||
// Use of this source code is governed by the Polyform Free Trial License
|
//
|
||||||
// that can be found in the LICENSE.md file for this repository.
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build wireinject
|
//go:build wireinject
|
||||||
// +build wireinject
|
// +build wireinject
|
||||||
|
@ -63,7 +63,7 @@ import (
|
|||||||
"github.com/harness/gitness/app/pipeline/runner"
|
"github.com/harness/gitness/app/pipeline/runner"
|
||||||
"github.com/harness/gitness/app/pipeline/scheduler"
|
"github.com/harness/gitness/app/pipeline/scheduler"
|
||||||
"github.com/harness/gitness/app/pipeline/triggerer"
|
"github.com/harness/gitness/app/pipeline/triggerer"
|
||||||
"github.com/harness/gitness/app/router"
|
router2 "github.com/harness/gitness/app/router"
|
||||||
server2 "github.com/harness/gitness/app/server"
|
server2 "github.com/harness/gitness/app/server"
|
||||||
"github.com/harness/gitness/app/services"
|
"github.com/harness/gitness/app/services"
|
||||||
"github.com/harness/gitness/app/services/aiagent"
|
"github.com/harness/gitness/app/services/aiagent"
|
||||||
@ -113,6 +113,12 @@ import (
|
|||||||
"github.com/harness/gitness/livelog"
|
"github.com/harness/gitness/livelog"
|
||||||
"github.com/harness/gitness/lock"
|
"github.com/harness/gitness/lock"
|
||||||
"github.com/harness/gitness/pubsub"
|
"github.com/harness/gitness/pubsub"
|
||||||
|
api2 "github.com/harness/gitness/registry/app/api"
|
||||||
|
"github.com/harness/gitness/registry/app/api/router"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
database2 "github.com/harness/gitness/registry/app/store/database"
|
||||||
|
"github.com/harness/gitness/registry/gc"
|
||||||
"github.com/harness/gitness/ssh"
|
"github.com/harness/gitness/ssh"
|
||||||
"github.com/harness/gitness/store/database/dbtx"
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
@ -396,7 +402,36 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||||||
}
|
}
|
||||||
aiagentController := aiagent2.ProvideController(authorizer, harnessIntelligence, repoStore, pipelineStore, executionStore)
|
aiagentController := aiagent2.ProvideController(authorizer, harnessIntelligence, repoStore, pipelineStore, executionStore)
|
||||||
openapiService := openapi.ProvideOpenAPIService()
|
openapiService := openapi.ProvideOpenAPIService()
|
||||||
routerRouter := router.ProvideRouter(ctx, config, authenticator, repoController, reposettingsController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController, infraproviderController, gitspaceController, migrateController, aiagentController, capabilitiesController, provider, openapiService)
|
storageDriver, err := api2.BlobStorageProvider(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
storageDeleter := gc.StorageDeleterProvider(storageDriver)
|
||||||
|
mediaTypesRepository := database2.ProvideMediaTypeDao(db)
|
||||||
|
blobRepository := database2.ProvideBlobDao(db, mediaTypesRepository)
|
||||||
|
storageService := docker.StorageServiceProvider(config, storageDriver)
|
||||||
|
manifestRepository := database2.ProvideManifestDao(db, mediaTypesRepository)
|
||||||
|
gcService := gc.ServiceProvider()
|
||||||
|
app := docker.NewApp(ctx, db, storageDeleter, blobRepository, spaceStore, config, storageService, mediaTypesRepository, manifestRepository, gcService)
|
||||||
|
registryRepository := database2.ProvideRepoDao(db, mediaTypesRepository)
|
||||||
|
manifestReferenceRepository := database2.ProvideManifestRefDao(db)
|
||||||
|
tagRepository := database2.ProvideTagDao(db)
|
||||||
|
artifactRepository := database2.ProvideArtifactDao(db)
|
||||||
|
artifactStatRepository := database2.ProvideArtifactStatDao(db)
|
||||||
|
layerRepository := database2.ProvideLayerDao(db, mediaTypesRepository)
|
||||||
|
manifestService := docker.ManifestServiceProvider(registryRepository, manifestRepository, blobRepository, mediaTypesRepository, manifestReferenceRepository, tagRepository, artifactRepository, artifactStatRepository, layerRepository, gcService, transactor)
|
||||||
|
registryBlobRepository := database2.ProvideRegistryBlobDao(db)
|
||||||
|
localRegistry := docker.LocalRegistryProvider(app, manifestService, blobRepository, registryRepository, manifestRepository, registryBlobRepository, mediaTypesRepository, tagRepository, artifactRepository, artifactStatRepository, gcService, transactor)
|
||||||
|
upstreamProxyConfigRepository := database2.ProvideUpstreamDao(db, registryRepository)
|
||||||
|
remoteRegistry := docker.RemoteRegistryProvider(localRegistry, app, upstreamProxyConfigRepository, secretStore, encrypter)
|
||||||
|
coreController := pkg.CoreControllerProvider(registryRepository)
|
||||||
|
dockerController := docker.ControllerProvider(localRegistry, remoteRegistry, coreController, spaceStore, authorizer)
|
||||||
|
handler := api2.NewHandlerProvider(dockerController, spaceStore, tokenStore, controller, authenticator, provider, authorizer)
|
||||||
|
registryOCIHandler := router.OCIHandlerProvider(handler)
|
||||||
|
cleanupPolicyRepository := database2.ProvideCleanupPolicyDao(db, transactor)
|
||||||
|
apiHandler := router.APIHandlerProvider(registryRepository, upstreamProxyConfigRepository, tagRepository, manifestRepository, cleanupPolicyRepository, artifactRepository, storageDriver, spaceStore, transactor, authenticator, provider, authorizer, auditService)
|
||||||
|
appRouter := router.AppRouterProvider(registryOCIHandler, apiHandler)
|
||||||
|
routerRouter := router2.ProvideRouter(ctx, config, authenticator, repoController, reposettingsController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController, infraproviderController, gitspaceController, migrateController, aiagentController, capabilitiesController, provider, openapiService, appRouter)
|
||||||
serverServer := server2.ProvideServer(config, routerRouter)
|
serverServer := server2.ProvideServer(config, routerRouter)
|
||||||
publickeyService := publickey.ProvidePublicKey(publicKeyStore, principalInfoCache)
|
publickeyService := publickey.ProvidePublicKey(publicKeyStore, principalInfoCache)
|
||||||
sshServer := ssh.ProvideServer(config, publickeyService, repoController)
|
sshServer := ssh.ProvideServer(config, publickeyService, repoController)
|
||||||
|
44
go.mod
44
go.mod
@ -10,6 +10,8 @@ require (
|
|||||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||||
github.com/coreos/go-semver v0.3.1
|
github.com/coreos/go-semver v0.3.1
|
||||||
github.com/dchest/uniuri v1.2.0
|
github.com/dchest/uniuri v1.2.0
|
||||||
|
github.com/distribution/distribution/v3 v3.0.0-alpha.1
|
||||||
|
github.com/distribution/reference v0.6.0
|
||||||
github.com/docker/docker v27.1.1+incompatible
|
github.com/docker/docker v27.1.1+incompatible
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/drone-runners/drone-runner-docker v1.8.4-0.20240815103043-c6c3a3e33ce3
|
github.com/drone-runners/drone-runner-docker v1.8.4-0.20240815103043-c6c3a3e33ce3
|
||||||
@ -21,23 +23,30 @@ require (
|
|||||||
github.com/drone/go-scm v1.38.4
|
github.com/drone/go-scm v1.38.4
|
||||||
github.com/drone/runner-go v1.12.0
|
github.com/drone/runner-go v1.12.0
|
||||||
github.com/drone/spec v0.0.0-20230920145636-3827abdce961
|
github.com/drone/spec v0.0.0-20230920145636-3827abdce961
|
||||||
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/fatih/color v1.17.0
|
github.com/fatih/color v1.17.0
|
||||||
github.com/gabriel-vasile/mimetype v1.4.4
|
github.com/gabriel-vasile/mimetype v1.4.4
|
||||||
|
github.com/getkin/kin-openapi v0.123.0
|
||||||
github.com/gliderlabs/ssh v0.3.7
|
github.com/gliderlabs/ssh v0.3.7
|
||||||
github.com/go-chi/chi v1.5.5
|
github.com/go-chi/chi v1.5.5
|
||||||
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/go-redsync/redsync/v4 v4.13.0
|
github.com/go-redsync/redsync/v4 v4.13.0
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.1
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/go-jsonnet v0.20.0
|
github.com/google/go-jsonnet v0.20.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/google/wire v0.6.0
|
github.com/google/wire v0.6.0
|
||||||
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
|
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
|
||||||
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/gotidy/ptr v1.4.0
|
github.com/gotidy/ptr v1.4.0
|
||||||
github.com/guregu/null v4.0.0+incompatible
|
github.com/guregu/null v4.0.0+incompatible
|
||||||
github.com/harness/harness-migrate v0.21.1-0.20240804180936-b1de602aa8e7
|
github.com/harness/harness-migrate v0.21.1-0.20240804180936-b1de602aa8e7
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
|
||||||
|
github.com/jackc/pgx/v5 v5.5.5
|
||||||
github.com/jmoiron/sqlx v1.4.0
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
@ -47,6 +56,9 @@ require (
|
|||||||
github.com/matoous/go-nanoid/v2 v2.1.0
|
github.com/matoous/go-nanoid/v2 v2.1.0
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
|
github.com/oapi-codegen/runtime v1.1.1
|
||||||
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
|
github.com/opencontainers/image-spec v1.1.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rs/xid v1.5.0
|
github.com/rs/xid v1.5.0
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
@ -55,6 +67,8 @@ require (
|
|||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/swaggest/openapi-go v0.2.23
|
github.com/swaggest/openapi-go v0.2.23
|
||||||
github.com/swaggest/swgui v1.8.1
|
github.com/swaggest/swgui v1.8.1
|
||||||
|
github.com/swaggo/http-swagger v1.3.4
|
||||||
|
github.com/swaggo/swag v1.16.2
|
||||||
github.com/unrolled/secure v1.15.0
|
github.com/unrolled/secure v1.15.0
|
||||||
github.com/zricethezav/gitleaks/v8 v8.18.5-0.20240614204812-26f34692fac6
|
github.com/zricethezav/gitleaks/v8 v8.18.5-0.20240614204812-26f34692fac6
|
||||||
go.starlark.net v0.0.0-20231121155337-90ade8b19d09
|
go.starlark.net v0.0.0-20231121155337-90ade8b19d09
|
||||||
@ -78,19 +92,18 @@ require (
|
|||||||
cloud.google.com/go/iam v1.1.12 // indirect
|
cloud.google.com/go/iam v1.1.12 // indirect
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e // indirect
|
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
|
||||||
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
|
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
|
||||||
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/antonmedv/expr v1.15.5 // indirect
|
github.com/antonmedv/expr v1.15.5 // indirect
|
||||||
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||||
github.com/buildkite/yaml v2.1.0+incompatible // indirect
|
github.com/buildkite/yaml v2.1.0+incompatible // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
|
||||||
github.com/charmbracelet/lipgloss v0.12.1 // indirect
|
github.com/charmbracelet/lipgloss v0.12.1 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.1.4 // indirect
|
github.com/charmbracelet/x/ansi v0.1.4 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/drone/envsubst v1.0.3 // indirect
|
github.com/drone/envsubst v1.0.3 // indirect
|
||||||
github.com/fatih/semgroup v1.2.0 // indirect
|
github.com/fatih/semgroup v1.2.0 // indirect
|
||||||
@ -99,6 +112,10 @@ require (
|
|||||||
github.com/ghodss/yaml v1.0.0 // indirect
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
github.com/gitleaks/go-gitdiff v0.9.0 // indirect
|
github.com/gitleaks/go-gitdiff v0.9.0 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||||
|
github.com/go-openapi/spec v0.20.9 // indirect
|
||||||
|
github.com/go-openapi/swag v0.22.8 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/google/s2a-go v0.1.8 // indirect
|
github.com/google/s2a-go v0.1.8 // indirect
|
||||||
@ -106,26 +123,29 @@ require (
|
|||||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||||
github.com/h2non/filetype v1.1.3 // indirect
|
github.com/h2non/filetype v1.1.3 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jackc/pgx/v4 v4.12.0 // indirect
|
github.com/invopop/yaml v0.2.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||||
github.com/muesli/termenv v0.15.2 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 // indirect
|
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 // indirect
|
||||||
github.com/onsi/gomega v1.27.10 // indirect
|
github.com/onsi/gomega v1.27.10 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
|
||||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
|
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.55.0 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
@ -134,14 +154,14 @@ require (
|
|||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/spf13/viper v1.19.0 // indirect
|
github.com/spf13/viper v1.19.0 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240723171418-e6d459c13d2a // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240723171418-e6d459c13d2a // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a // indirect
|
||||||
@ -180,10 +200,12 @@ require (
|
|||||||
github.com/vearutop/statigz v1.4.0 // indirect
|
github.com/vearutop/statigz v1.4.0 // indirect
|
||||||
github.com/yuin/goldmark v1.4.13
|
github.com/yuin/goldmark v1.4.13
|
||||||
golang.org/x/mod v0.19.0 // indirect
|
golang.org/x/mod v0.19.0 // indirect
|
||||||
golang.org/x/net v0.27.0 // indirect
|
golang.org/x/net v0.27.0
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
golang.org/x/tools v0.23.0 // indirect
|
golang.org/x/tools v0.23.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect
|
google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/harness/gitness/registry => ./registry
|
||||||
|
116
go.sum
116
go.sum
@ -30,12 +30,15 @@ github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8
|
|||||||
github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE=
|
github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
||||||
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
|
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||||
@ -57,6 +60,8 @@ github.com/antonmedv/expr v1.15.5 h1:y0Iz3cEwmpRz5/r3w4qQR0MfIqJGdGM1zbhD/v0G5Vg
|
|||||||
github.com/antonmedv/expr v1.15.5/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE=
|
github.com/antonmedv/expr v1.15.5/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||||
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
@ -85,9 +90,10 @@ github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6M
|
|||||||
github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8=
|
github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8=
|
||||||
github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI=
|
github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI=
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
@ -115,6 +121,7 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
|||||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
@ -124,6 +131,8 @@ github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/distribution/distribution/v3 v3.0.0-alpha.1 h1:jn7I1gvjOvmLztH1+1cLiUFud7aeJCIQcgzugtwjyJo=
|
||||||
|
github.com/distribution/distribution/v3 v3.0.0-alpha.1/go.mod h1:LCp4JZp1ZalYg0W/TN05jarCQu+h4w7xc7ZfQF4Y/cY=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o=
|
github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o=
|
||||||
@ -132,8 +141,9 @@ github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQ
|
|||||||
github.com/djherbis/nio/v3 v3.0.1 h1:6wxhnuppteMa6RHA4L81Dq7ThkZH8SwnDzXDYy95vB4=
|
github.com/djherbis/nio/v3 v3.0.1 h1:6wxhnuppteMa6RHA4L81Dq7ThkZH8SwnDzXDYy95vB4=
|
||||||
github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmWgZxOcmg=
|
github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmWgZxOcmg=
|
||||||
github.com/docker/distribution v0.0.0-20170726174610-edc3ab29cdff/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v0.0.0-20170726174610-edc3ab29cdff/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||||
|
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
|
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
|
||||||
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
@ -167,6 +177,8 @@ github.com/drone/signal v1.0.0/go.mod h1:S8t92eFT0g4WUgEc/LxG+LCuiskpMNsG0ajAMGn
|
|||||||
github.com/drone/spec v0.0.0-20230920145636-3827abdce961 h1:aUWrLS2ghyxIpDICpZOV50V1x7JLM3U80UQDQxMKT54=
|
github.com/drone/spec v0.0.0-20230920145636-3827abdce961 h1:aUWrLS2ghyxIpDICpZOV50V1x7JLM3U80UQDQxMKT54=
|
||||||
github.com/drone/spec v0.0.0-20230920145636-3827abdce961/go.mod h1:KyQZA9qwuscbbM7yTrtZg25Wammoc5GKwaRem8kDA5k=
|
github.com/drone/spec v0.0.0-20230920145636-3827abdce961/go.mod h1:KyQZA9qwuscbbM7yTrtZg25Wammoc5GKwaRem8kDA5k=
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
@ -192,6 +204,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
|
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
|
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
|
||||||
|
github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
|
||||||
|
github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gitleaks/go-gitdiff v0.9.0 h1:SHAU2l0ZBEo8g82EeFewhVy81sb7JCxW76oSPtR/Nqg=
|
github.com/gitleaks/go-gitdiff v0.9.0 h1:SHAU2l0ZBEo8g82EeFewhVy81sb7JCxW76oSPtR/Nqg=
|
||||||
@ -200,6 +214,8 @@ github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
|||||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||||
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
|
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
|
||||||
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
@ -215,6 +231,18 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs=
|
github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs=
|
||||||
github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho=
|
github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||||
|
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||||
|
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
|
||||||
|
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||||
|
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
|
||||||
|
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||||
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
|
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
|
||||||
|
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
|
||||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||||
github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI=
|
github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI=
|
||||||
@ -228,9 +256,10 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
|||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||||
|
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
|
||||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||||
github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
@ -240,6 +269,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@ -305,6 +336,8 @@ github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b0
|
|||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gotidy/ptr v1.4.0 h1:7++suUs+HNHMnyz6/AW3SE+4EnBhupPSQTSI7QNijVc=
|
github.com/gotidy/ptr v1.4.0 h1:7++suUs+HNHMnyz6/AW3SE+4EnBhupPSQTSI7QNijVc=
|
||||||
github.com/gotidy/ptr v1.4.0/go.mod h1:MjRBG6/IETiiZGWI8LrRtISXEji+8b/jigmj2q0mEyM=
|
github.com/gotidy/ptr v1.4.0/go.mod h1:MjRBG6/IETiiZGWI8LrRtISXEji+8b/jigmj2q0mEyM=
|
||||||
@ -359,6 +392,8 @@ github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36
|
|||||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
|
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
|
||||||
|
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
@ -370,14 +405,14 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
|
|||||||
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
||||||
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
|
||||||
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
|
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
|
||||||
github.com/jackc/pgconn v1.9.0 h1:gqibKSTJup/ahCsNKyMZAniPuZEfIqfXFc8FOWVYR+Q=
|
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||||
@ -389,11 +424,12 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:
|
|||||||
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.1.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.1.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
|
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
@ -401,8 +437,8 @@ github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4
|
|||||||
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
||||||
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||||
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
|
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
|
||||||
github.com/jackc/pgtype v1.8.0 h1:iFVCcVhYlw0PulYCVoguRGm0SE9guIcPcccnLzHj8bA=
|
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
|
||||||
github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
|
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
@ -410,8 +446,10 @@ github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXg
|
|||||||
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
||||||
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||||
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
|
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
|
||||||
github.com/jackc/pgx/v4 v4.12.0 h1:xiP3TdnkwyslWNp77yE5XAPfxAsU9RMFDe0c1SwN8h4=
|
github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU=
|
||||||
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
|
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||||
|
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||||
|
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
@ -427,11 +465,14 @@ github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT
|
|||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||||
@ -457,7 +498,6 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
@ -467,6 +507,11 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
|
|||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/maragudk/migrate v0.4.3 h1:3NrpSzNdCSSPgN/xwkEduEwqrBIRewSEvtN+mhMS6zc=
|
github.com/maragudk/migrate v0.4.3 h1:3NrpSzNdCSSPgN/xwkEduEwqrBIRewSEvtN+mhMS6zc=
|
||||||
github.com/maragudk/migrate v0.4.3/go.mod h1:vhmL4s+Xz75KU6DPZWRfqb45YyqjYQfcXliA1DsYzvY=
|
github.com/maragudk/migrate v0.4.3/go.mod h1:vhmL4s+Xz75KU6DPZWRfqb45YyqjYQfcXliA1DsYzvY=
|
||||||
github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek=
|
github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek=
|
||||||
@ -517,6 +562,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||||
@ -534,8 +581,11 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
|
|||||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||||
|
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
@ -567,6 +617,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||||
|
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||||
|
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||||
github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4=
|
github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||||
@ -614,8 +666,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
@ -639,7 +691,6 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
|||||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
@ -663,6 +714,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||||
@ -676,6 +728,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
@ -697,7 +750,15 @@ github.com/swaggest/refl v1.1.0 h1:a+9a75Kv6ciMozPjVbOfcVTEQe81t2R3emvaD9oGQGc=
|
|||||||
github.com/swaggest/refl v1.1.0/go.mod h1:g3Qa6ki0A/L2yxiuUpT+cuBURuRaltF5SDQpg1kMZSY=
|
github.com/swaggest/refl v1.1.0/go.mod h1:g3Qa6ki0A/L2yxiuUpT+cuBURuRaltF5SDQpg1kMZSY=
|
||||||
github.com/swaggest/swgui v1.8.1 h1:OLcigpoelY0spbpvp6WvBt0I1z+E9egMQlUeEKya+zU=
|
github.com/swaggest/swgui v1.8.1 h1:OLcigpoelY0spbpvp6WvBt0I1z+E9egMQlUeEKya+zU=
|
||||||
github.com/swaggest/swgui v1.8.1/go.mod h1:YBaAVAwS3ndfvdtW8A4yWDJpge+W57y+8kW+f/DqZtU=
|
github.com/swaggest/swgui v1.8.1/go.mod h1:YBaAVAwS3ndfvdtW8A4yWDJpge+W57y+8kW+f/DqZtU=
|
||||||
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
|
||||||
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
|
||||||
|
github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=
|
||||||
|
github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=
|
||||||
|
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
|
||||||
|
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/unrolled/secure v1.15.0 h1:q7x+pdp8jAHnbzxu6UheP8fRlG/rwYTb8TPuQ3rn9Og=
|
github.com/unrolled/secure v1.15.0 h1:q7x+pdp8jAHnbzxu6UheP8fRlG/rwYTb8TPuQ3rn9Og=
|
||||||
github.com/unrolled/secure v1.15.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
github.com/unrolled/secure v1.15.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
@ -730,8 +791,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0
|
|||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc=
|
||||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 h1:1wp/gyxsuYtuE/JFxsQRtcCDtMrO2qMvlfXALU5wkzI=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 h1:1wp/gyxsuYtuE/JFxsQRtcCDtMrO2qMvlfXALU5wkzI=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0/go.mod h1:gbTHmghkGgqxMomVQQMur1Nba4M0MQ8AYThXDUjsJ38=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0/go.mod h1:gbTHmghkGgqxMomVQQMur1Nba4M0MQ8AYThXDUjsJ38=
|
||||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
@ -748,6 +809,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
|
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
@ -770,10 +833,8 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
@ -818,6 +879,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
@ -861,14 +923,13 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -880,7 +941,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
@ -892,7 +952,6 @@ golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
|||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
@ -986,6 +1045,7 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
|
|||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
@ -1009,6 +1069,8 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
280
registry/app/api/controller/metadata/artifact_mapper.go
Normal file
280
registry/app/api/controller/metadata/artifact_mapper.go
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
artifactapi "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetArtifactMetadata(artifacts *[]types.ArtifactMetadata) []artifactapi.ArtifactMetadata {
|
||||||
|
artifactMetadataList := make([]artifactapi.ArtifactMetadata, 0, len(*artifacts))
|
||||||
|
for _, artifact := range *artifacts {
|
||||||
|
artifactMetadata := mapToArtifactMetadata(artifact)
|
||||||
|
artifactMetadataList = append(artifactMetadataList, *artifactMetadata)
|
||||||
|
}
|
||||||
|
return artifactMetadataList
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToArtifactMetadata(artifact types.ArtifactMetadata) *artifactapi.ArtifactMetadata {
|
||||||
|
lastModified := GetTimeInMs(artifact.ModifiedAt)
|
||||||
|
packageType := artifact.PackageType
|
||||||
|
return &artifactapi.ArtifactMetadata{
|
||||||
|
RegistryIdentifier: artifact.RepoName,
|
||||||
|
Name: artifact.Name,
|
||||||
|
LatestVersion: artifact.LatestVersion,
|
||||||
|
Labels: &artifact.Labels,
|
||||||
|
LastModified: &lastModified,
|
||||||
|
PackageType: &packageType,
|
||||||
|
DownloadsCount: &artifact.DownloadCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toPackageType(packageTypeStr string) (artifactapi.PackageType, error) {
|
||||||
|
switch packageTypeStr {
|
||||||
|
case string(artifactapi.PackageTypeDOCKER):
|
||||||
|
return artifactapi.PackageTypeDOCKER, nil
|
||||||
|
case string(artifactapi.PackageTypeGENERIC):
|
||||||
|
return artifactapi.PackageTypeGENERIC, nil
|
||||||
|
case string(artifactapi.PackageTypeHELM):
|
||||||
|
return artifactapi.PackageTypeHELM, nil
|
||||||
|
case string(artifactapi.PackageTypeMAVEN):
|
||||||
|
return artifactapi.PackageTypeMAVEN, nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("invalid package type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTagMetadata(
|
||||||
|
ctx context.Context,
|
||||||
|
tags *[]types.TagMetadata,
|
||||||
|
latestTag string,
|
||||||
|
image string,
|
||||||
|
regIdentifier string,
|
||||||
|
rootIdentifier string,
|
||||||
|
registryURL string,
|
||||||
|
) []artifactapi.ArtifactVersionMetadata {
|
||||||
|
artifactVersionMetadataList := []artifactapi.ArtifactVersionMetadata{}
|
||||||
|
digestCount := int64(1)
|
||||||
|
for _, tag := range *tags {
|
||||||
|
modifiedAt := GetTimeInMs(tag.ModifiedAt)
|
||||||
|
size := GetImageSize(tag.Size)
|
||||||
|
isLatestVersion := latestTag == tag.Name
|
||||||
|
command := GetPullCommand(rootIdentifier, regIdentifier, image, tag.Name, string(tag.PackageType), registryURL)
|
||||||
|
packageType, err := toPackageType(string(tag.PackageType))
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Error().Err(err).Msgf("Error converting package type %s", tag.PackageType)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
artifactVersionMetadata := &artifactapi.ArtifactVersionMetadata{
|
||||||
|
PackageType: &packageType,
|
||||||
|
Name: tag.Name,
|
||||||
|
Size: &size,
|
||||||
|
LastModified: &modifiedAt,
|
||||||
|
DigestCount: &digestCount,
|
||||||
|
IslatestVersion: &isLatestVersion,
|
||||||
|
PullCommand: &command,
|
||||||
|
}
|
||||||
|
artifactVersionMetadataList = append(artifactVersionMetadataList, *artifactVersionMetadata)
|
||||||
|
}
|
||||||
|
return artifactVersionMetadataList
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllArtifactResponse(
|
||||||
|
artifacts *[]types.ArtifactMetadata,
|
||||||
|
count int64,
|
||||||
|
pageNumber int64,
|
||||||
|
pageSize int,
|
||||||
|
) *artifactapi.ListArtifactResponseJSONResponse {
|
||||||
|
artifactMetadataList := GetArtifactMetadata(artifacts)
|
||||||
|
pageCount := GetPageCount(count, pageSize)
|
||||||
|
listArtifact := &artifactapi.ListArtifact{
|
||||||
|
ItemCount: &count,
|
||||||
|
PageCount: &pageCount,
|
||||||
|
PageIndex: &pageNumber,
|
||||||
|
PageSize: &pageSize,
|
||||||
|
Artifacts: artifactMetadataList,
|
||||||
|
}
|
||||||
|
response := &artifactapi.ListArtifactResponseJSONResponse{
|
||||||
|
Data: *listArtifact,
|
||||||
|
Status: artifactapi.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllArtifactLabelsResponse(
|
||||||
|
artifactLabels *[]string,
|
||||||
|
count int64,
|
||||||
|
pageNumber int64,
|
||||||
|
pageSize int,
|
||||||
|
) *artifactapi.ListArtifactLabelResponseJSONResponse {
|
||||||
|
pageCount := GetPageCount(count, pageSize)
|
||||||
|
listArtifactLabels := &artifactapi.ListArtifactLabel{
|
||||||
|
ItemCount: &count,
|
||||||
|
PageCount: &pageCount,
|
||||||
|
PageIndex: &pageNumber,
|
||||||
|
PageSize: &pageSize,
|
||||||
|
Labels: *artifactLabels,
|
||||||
|
}
|
||||||
|
response := &artifactapi.ListArtifactLabelResponseJSONResponse{
|
||||||
|
Data: *listArtifactLabels,
|
||||||
|
Status: artifactapi.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllArtifactVersionResponse(
|
||||||
|
ctx context.Context,
|
||||||
|
tags *[]types.TagMetadata,
|
||||||
|
latestTag string,
|
||||||
|
image string,
|
||||||
|
count int64,
|
||||||
|
regInfo *RegistryRequestInfo,
|
||||||
|
pageNumber int64,
|
||||||
|
pageSize int,
|
||||||
|
rootIdentifier string,
|
||||||
|
registryURL string,
|
||||||
|
) *artifactapi.ListArtifactVersionResponseJSONResponse {
|
||||||
|
artifactVersionMetadataList := GetTagMetadata(
|
||||||
|
ctx, tags, latestTag, image,
|
||||||
|
regInfo.RegistryIdentifier, rootIdentifier, registryURL,
|
||||||
|
)
|
||||||
|
pageCount := GetPageCount(count, pageSize)
|
||||||
|
listArtifactVersions := &artifactapi.ListArtifactVersion{
|
||||||
|
ItemCount: &count,
|
||||||
|
PageCount: &pageCount,
|
||||||
|
PageIndex: &pageNumber,
|
||||||
|
PageSize: &pageSize,
|
||||||
|
ArtifactVersions: &artifactVersionMetadataList,
|
||||||
|
}
|
||||||
|
response := &artifactapi.ListArtifactVersionResponseJSONResponse{
|
||||||
|
Data: *listArtifactVersions,
|
||||||
|
Status: artifactapi.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDockerArtifactDetails(
|
||||||
|
registry *types.Registry,
|
||||||
|
tag *types.TagDetail,
|
||||||
|
manifest *types.Manifest,
|
||||||
|
isLatestTag bool,
|
||||||
|
regInfo *RegistryRequestBaseInfo,
|
||||||
|
registryURL string,
|
||||||
|
) *artifactapi.DockerArtifactDetailResponseJSONResponse {
|
||||||
|
repoPath := getRepoPath(registry.Name, tag.ImageName, manifest.Digest.String())
|
||||||
|
pullCommand := GetDockerPullCommand(regInfo.rootIdentifier, registry.Name, tag.ImageName, tag.Name, registryURL)
|
||||||
|
createdAt := GetTimeInMs(tag.CreatedAt)
|
||||||
|
modifiedAt := GetTimeInMs(tag.UpdatedAt)
|
||||||
|
size := GetSize(manifest.TotalSize)
|
||||||
|
artifactDetail := &artifactapi.DockerArtifactDetail{
|
||||||
|
ImageName: tag.ImageName,
|
||||||
|
Version: tag.Name,
|
||||||
|
PackageType: registry.PackageType,
|
||||||
|
IsLatestVersion: &isLatestTag,
|
||||||
|
CreatedAt: &createdAt,
|
||||||
|
ModifiedAt: &modifiedAt,
|
||||||
|
RegistryPath: repoPath,
|
||||||
|
PullCommand: &pullCommand,
|
||||||
|
Url: GetTagURL(regInfo.rootIdentifier, tag.ImageName, tag.Name, registry.Name, registryURL),
|
||||||
|
Size: &size,
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &artifactapi.DockerArtifactDetailResponseJSONResponse{
|
||||||
|
Data: *artifactDetail,
|
||||||
|
Status: artifactapi.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHelmArtifactDetails(
|
||||||
|
registry *types.Registry,
|
||||||
|
tag *types.TagDetail,
|
||||||
|
manifest *types.Manifest,
|
||||||
|
isLatestTag bool,
|
||||||
|
rootIdentifier string,
|
||||||
|
registryURL string,
|
||||||
|
) *artifactapi.HelmArtifactDetailResponseJSONResponse {
|
||||||
|
repoPath := getRepoPath(registry.Name, tag.ImageName, manifest.Digest.String())
|
||||||
|
pullCommand := GetHelmPullCommand(rootIdentifier, registry.Name, tag.ImageName, tag.Name, registryURL)
|
||||||
|
createdAt := GetTimeInMs(tag.CreatedAt)
|
||||||
|
modifiedAt := GetTimeInMs(tag.UpdatedAt)
|
||||||
|
size := GetSize(manifest.TotalSize)
|
||||||
|
artifactDetail := &artifactapi.HelmArtifactDetail{
|
||||||
|
Artifact: &tag.ImageName,
|
||||||
|
Version: tag.Name,
|
||||||
|
PackageType: registry.PackageType,
|
||||||
|
IsLatestVersion: &isLatestTag,
|
||||||
|
CreatedAt: &createdAt,
|
||||||
|
ModifiedAt: &modifiedAt,
|
||||||
|
RegistryPath: repoPath,
|
||||||
|
PullCommand: &pullCommand,
|
||||||
|
Url: GetTagURL(rootIdentifier, tag.ImageName, tag.Name, registry.Name, registryURL),
|
||||||
|
Size: &size,
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &artifactapi.HelmArtifactDetailResponseJSONResponse{
|
||||||
|
Data: *artifactDetail,
|
||||||
|
Status: artifactapi.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetArtifactSummary(artifact types.ArtifactMetadata) *artifactapi.ArtifactSummaryResponseJSONResponse {
|
||||||
|
downloads := int64(0)
|
||||||
|
createdAt := GetTimeInMs(artifact.CreatedAt)
|
||||||
|
modifiedAt := GetTimeInMs(artifact.ModifiedAt)
|
||||||
|
artifactVersionSummary := &artifactapi.ArtifactSummary{
|
||||||
|
CreatedAt: &createdAt,
|
||||||
|
ModifiedAt: &modifiedAt,
|
||||||
|
DownloadsCount: &downloads,
|
||||||
|
ImageName: artifact.Name,
|
||||||
|
Labels: &artifact.Labels,
|
||||||
|
PackageType: artifact.PackageType,
|
||||||
|
}
|
||||||
|
response := &artifactapi.ArtifactSummaryResponseJSONResponse{
|
||||||
|
Data: *artifactVersionSummary,
|
||||||
|
Status: artifactapi.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetArtifactVersionSummary(
|
||||||
|
tag *types.TagMetadata,
|
||||||
|
artifactName string,
|
||||||
|
isLatestTag bool,
|
||||||
|
) *artifactapi.ArtifactVersionSummaryResponseJSONResponse {
|
||||||
|
artifactVersionSummary := &artifactapi.ArtifactVersionSummary{
|
||||||
|
ImageName: artifactName,
|
||||||
|
IsLatestVersion: &isLatestTag,
|
||||||
|
PackageType: tag.PackageType,
|
||||||
|
Version: tag.Name,
|
||||||
|
}
|
||||||
|
response := &artifactapi.ArtifactVersionSummaryResponseJSONResponse{
|
||||||
|
Data: *artifactVersionSummary,
|
||||||
|
Status: artifactapi.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRepoPath(registry string, image string, tag string) string {
|
||||||
|
return filepath.Join(registry, image, tag)
|
||||||
|
}
|
349
registry/app/api/controller/metadata/base.go
Normal file
349
registry/app/api/controller/metadata/base.go
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/paths"
|
||||||
|
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
storagedriver "github.com/harness/gitness/registry/app/driver"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
"github.com/harness/gitness/registry/app/storage"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MediaTypeImageConfig = "application/vnd.docker.container.image.v1+json"
|
||||||
|
|
||||||
|
var _ api.StrictServerInterface = (*APIController)(nil)
|
||||||
|
|
||||||
|
type RegistryRequestBaseInfo struct {
|
||||||
|
rootIdentifier string
|
||||||
|
rootIdentifierID int64
|
||||||
|
|
||||||
|
registryRef string
|
||||||
|
RegistryIdentifier string
|
||||||
|
registryID int64
|
||||||
|
|
||||||
|
parentRef string
|
||||||
|
parentID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegistryRequestInfo struct {
|
||||||
|
RegistryRequestBaseInfo
|
||||||
|
packageTypes []string
|
||||||
|
sortByField string
|
||||||
|
sortByOrder string
|
||||||
|
offset int
|
||||||
|
limit int
|
||||||
|
pageNumber int64
|
||||||
|
searchTerm string
|
||||||
|
labels []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRegistryRequestBaseInfo returns the base info for the registry request
|
||||||
|
// One of the regRefParam or (parentRefParam + regIdentifierParam) should be provided.
|
||||||
|
func (c *APIController) GetRegistryRequestBaseInfo(
|
||||||
|
ctx context.Context,
|
||||||
|
parentRef string,
|
||||||
|
regRef string,
|
||||||
|
) (*RegistryRequestBaseInfo, error) {
|
||||||
|
// ---------- CHECKS ------------
|
||||||
|
if commons.IsEmpty(parentRef) && !commons.IsEmpty(regRef) {
|
||||||
|
parentRef, _, _ = paths.DisectLeaf(regRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- PARENT ------------
|
||||||
|
if commons.IsEmpty(parentRef) {
|
||||||
|
return nil, fmt.Errorf("parent reference is required")
|
||||||
|
}
|
||||||
|
rootIdentifier, _, err := paths.DisectRoot(parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid parent reference: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootSpace, err := c.spaceStore.FindByRef(ctx, rootIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("root space not found: %w", err)
|
||||||
|
}
|
||||||
|
parentSpace, err := c.spaceStore.FindByRef(ctx, parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parent space not found: %w", err)
|
||||||
|
}
|
||||||
|
rootIdentifierID := rootSpace.ID
|
||||||
|
parentID := parentSpace.ID
|
||||||
|
|
||||||
|
baseInfo := &RegistryRequestBaseInfo{
|
||||||
|
parentRef: parentRef,
|
||||||
|
parentID: parentID,
|
||||||
|
rootIdentifier: rootIdentifier,
|
||||||
|
rootIdentifierID: rootIdentifierID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- REGISTRY ------------
|
||||||
|
if !commons.IsEmpty(regRef) {
|
||||||
|
_, regIdentifier, _ := paths.DisectLeaf(regRef)
|
||||||
|
|
||||||
|
reg, getRegistryErr := c.RegistryRepository.GetByParentIDAndName(ctx, parentID, regIdentifier)
|
||||||
|
if getRegistryErr != nil {
|
||||||
|
return nil, fmt.Errorf("registry not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
baseInfo.registryRef = regRef
|
||||||
|
baseInfo.RegistryIdentifier = regIdentifier
|
||||||
|
baseInfo.registryID = reg.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) GetRegistryRequestInfo(
|
||||||
|
ctx context.Context,
|
||||||
|
packageTypesParam *api.PackageTypeParam,
|
||||||
|
page *api.PageNumber,
|
||||||
|
size *api.PageSize,
|
||||||
|
search *api.SearchTerm,
|
||||||
|
resource string,
|
||||||
|
parentRef string,
|
||||||
|
regRef string,
|
||||||
|
labelsParam *api.LabelsParam,
|
||||||
|
sortOrder *api.SortOrder,
|
||||||
|
sortField *api.SortField,
|
||||||
|
) (*RegistryRequestInfo, error) {
|
||||||
|
packageTypes := []string{}
|
||||||
|
if packageTypesParam != nil {
|
||||||
|
packageTypes = *packageTypesParam
|
||||||
|
}
|
||||||
|
sortByField := ""
|
||||||
|
sortByOrder := ""
|
||||||
|
if sortOrder != nil {
|
||||||
|
sortByOrder = string(*sortOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sortField != nil {
|
||||||
|
sortByField = string(*sortField)
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := []string{}
|
||||||
|
|
||||||
|
if labelsParam != nil {
|
||||||
|
labels = *labelsParam
|
||||||
|
}
|
||||||
|
|
||||||
|
sortByField = GetSortByField(sortByField, resource)
|
||||||
|
sortByOrder = GetSortByOrder(sortByOrder)
|
||||||
|
|
||||||
|
offset := GetOffset(size, page)
|
||||||
|
limit := GetPageLimit(size)
|
||||||
|
pageNumber := GetPageNumber(page)
|
||||||
|
|
||||||
|
searchTerm := ""
|
||||||
|
if search != nil {
|
||||||
|
searchTerm = string(*search)
|
||||||
|
}
|
||||||
|
|
||||||
|
baseInfo, err := c.GetRegistryRequestBaseInfo(ctx, parentRef, regRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RegistryRequestInfo{
|
||||||
|
RegistryRequestBaseInfo: *baseInfo,
|
||||||
|
packageTypes: packageTypes,
|
||||||
|
sortByField: sortByField,
|
||||||
|
sortByOrder: sortByOrder,
|
||||||
|
offset: offset,
|
||||||
|
limit: limit,
|
||||||
|
pageNumber: pageNumber,
|
||||||
|
searchTerm: searchTerm,
|
||||||
|
labels: labels,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getManifestConfig(
|
||||||
|
ctx context.Context,
|
||||||
|
digest digest.Digest,
|
||||||
|
rootRef string,
|
||||||
|
driver storagedriver.StorageDriver,
|
||||||
|
) (*manifestConfig, error) {
|
||||||
|
var config manifestConfig
|
||||||
|
path, err := storage.PathFn(rootRef, digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := driver.GetContent(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get content for image config: %w", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(content, &config); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal manifest config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) setUpstreamProxyIDs(
|
||||||
|
ctx context.Context,
|
||||||
|
registry *types.Registry,
|
||||||
|
dto api.RegistryRequest,
|
||||||
|
parentID int64,
|
||||||
|
) error {
|
||||||
|
if dto.Config.Type != api.RegistryTypeVIRTUAL {
|
||||||
|
return fmt.Errorf("invalid call to set upstream proxy ids for parentID: %d", parentID)
|
||||||
|
}
|
||||||
|
virtualConfig, err := dto.Config.AsVirtualConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get virtualConfig: %w", err)
|
||||||
|
}
|
||||||
|
if nil == virtualConfig.UpstreamProxies || commons.IsEmpty(*(virtualConfig.UpstreamProxies)) {
|
||||||
|
log.Ctx(ctx).Debug().Msgf("Nothing to do for registryRequest: %s", dto.Identifier)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
upstreamProxies, err := c.RegistryRepository.FetchUpstreamProxyIDs(
|
||||||
|
ctx,
|
||||||
|
*virtualConfig.UpstreamProxies,
|
||||||
|
parentID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fectch upstream proxy IDs :%w", err)
|
||||||
|
}
|
||||||
|
registry.UpstreamProxies = upstreamProxies
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) getUpstreamProxyKeys(ctx context.Context, ids []int64) []string {
|
||||||
|
repoKeys, _ := c.RegistryRepository.FetchUpstreamProxyKeys(ctx, ids)
|
||||||
|
return repoKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
type manifestConfig struct {
|
||||||
|
CreatedAt *string `json:"created,omitempty"`
|
||||||
|
Digest string `json:"digest,omitempty"`
|
||||||
|
History []historyEntry `json:"history"`
|
||||||
|
ModifiedAt *string `json:"modified,omitempty"`
|
||||||
|
Os string `json:"os"`
|
||||||
|
Arch string `json:"architecture,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type historyEntry struct {
|
||||||
|
Created string `json:"created"`
|
||||||
|
CreatedBy string `json:"created_by"`
|
||||||
|
EmptyLayer bool `json:"empty_layer"`
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRepoEntityFields(dto api.RegistryRequest) ([]string, []string, string, []string) {
|
||||||
|
allowedPattern := []string{}
|
||||||
|
if dto.AllowedPattern != nil {
|
||||||
|
allowedPattern = *dto.AllowedPattern
|
||||||
|
}
|
||||||
|
blockedPattern := []string{}
|
||||||
|
if dto.BlockedPattern != nil {
|
||||||
|
blockedPattern = *dto.BlockedPattern
|
||||||
|
}
|
||||||
|
description := ""
|
||||||
|
if dto.Description != nil {
|
||||||
|
description = *dto.Description
|
||||||
|
}
|
||||||
|
labels := []string{}
|
||||||
|
if dto.Labels != nil {
|
||||||
|
labels = *dto.Labels
|
||||||
|
}
|
||||||
|
return allowedPattern, blockedPattern, description, labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateVirtualRepositoryResponse(
|
||||||
|
registry *types.Registry,
|
||||||
|
upstreamProxyKeys []string,
|
||||||
|
cleanupPolicies *[]types.CleanupPolicy,
|
||||||
|
rootIdentifier string,
|
||||||
|
registryURL string,
|
||||||
|
) *api.RegistryResponseJSONResponse {
|
||||||
|
createdAt := GetTimeInMs(registry.CreatedAt)
|
||||||
|
modifiedAt := GetTimeInMs(registry.UpdatedAt)
|
||||||
|
allowedPattern := registry.AllowedPattern
|
||||||
|
blockedPattern := registry.BlockedPattern
|
||||||
|
labels := registry.Labels
|
||||||
|
|
||||||
|
config := api.RegistryConfig{}
|
||||||
|
_ = config.FromVirtualConfig(api.VirtualConfig{UpstreamProxies: &upstreamProxyKeys})
|
||||||
|
response := &api.RegistryResponseJSONResponse{
|
||||||
|
Data: api.Registry{
|
||||||
|
Identifier: registry.Name,
|
||||||
|
Description: ®istry.Description,
|
||||||
|
Url: GetRepoURL(rootIdentifier, registry.Name, registryURL),
|
||||||
|
PackageType: registry.PackageType,
|
||||||
|
AllowedPattern: &allowedPattern,
|
||||||
|
BlockedPattern: &blockedPattern,
|
||||||
|
CreatedAt: &createdAt,
|
||||||
|
ModifiedAt: &modifiedAt,
|
||||||
|
CleanupPolicy: CreateCleanupPolicyResponse(cleanupPolicies),
|
||||||
|
Config: &config,
|
||||||
|
Labels: &labels,
|
||||||
|
},
|
||||||
|
Status: api.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUpstreamProxyResponseJSONResponse(upstreamproxy *types.UpstreamProxy) *api.RegistryResponseJSONResponse {
|
||||||
|
createdAt := GetTimeInMs(upstreamproxy.CreatedAt)
|
||||||
|
modifiedAt := GetTimeInMs(upstreamproxy.UpdatedAt)
|
||||||
|
allowedPattern := upstreamproxy.AllowedPattern
|
||||||
|
blockedPattern := upstreamproxy.BlockedPattern
|
||||||
|
configAuth := &api.UpstreamConfig_Auth{}
|
||||||
|
|
||||||
|
if api.AuthType(upstreamproxy.RepoAuthType) == api.AuthTypeUserPassword {
|
||||||
|
auth := api.UserPassword{}
|
||||||
|
auth.UserName = upstreamproxy.UserName
|
||||||
|
// FIXME: Mask this password.
|
||||||
|
auth.SecretIdentifier = &upstreamproxy.SecretIdentifier
|
||||||
|
auth.SecretSpaceId = &upstreamproxy.SecretSpaceID
|
||||||
|
_ = configAuth.FromUserPassword(auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
source := api.UpstreamConfigSource(upstreamproxy.Source)
|
||||||
|
|
||||||
|
config := api.UpstreamConfig{
|
||||||
|
AuthType: api.AuthType(upstreamproxy.RepoAuthType),
|
||||||
|
Auth: configAuth,
|
||||||
|
Source: &source,
|
||||||
|
Url: &upstreamproxy.RepoURL,
|
||||||
|
}
|
||||||
|
registryConfig := &api.RegistryConfig{}
|
||||||
|
_ = registryConfig.FromUpstreamConfig(config)
|
||||||
|
|
||||||
|
response := &api.RegistryResponseJSONResponse{
|
||||||
|
Data: api.Registry{
|
||||||
|
Identifier: upstreamproxy.RepoKey,
|
||||||
|
PackageType: upstreamproxy.PackageType,
|
||||||
|
Url: upstreamproxy.RepoURL,
|
||||||
|
AllowedPattern: &allowedPattern,
|
||||||
|
BlockedPattern: &blockedPattern,
|
||||||
|
CreatedAt: &createdAt,
|
||||||
|
ModifiedAt: &modifiedAt,
|
||||||
|
Config: registryConfig,
|
||||||
|
},
|
||||||
|
Status: api.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
81
registry/app/api/controller/metadata/cleanuppolicy_mapper.go
Normal file
81
registry/app/api/controller/metadata/cleanuppolicy_mapper.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateCleanupPolicyEntity(
|
||||||
|
config *artifact.ModifyRegistryJSONRequestBody,
|
||||||
|
repoID int64,
|
||||||
|
) *[]types.CleanupPolicy {
|
||||||
|
if config == nil || config.CleanupPolicy == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cleanupPolicyEntities []types.CleanupPolicy
|
||||||
|
cleanupPolicyDto := *config.CleanupPolicy
|
||||||
|
|
||||||
|
for _, value := range cleanupPolicyDto {
|
||||||
|
cleanupPolicyEntity := getCleanupPolicyEntity(value, repoID)
|
||||||
|
cleanupPolicyEntities = append(cleanupPolicyEntities, *cleanupPolicyEntity)
|
||||||
|
}
|
||||||
|
return &cleanupPolicyEntities
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateCleanupPolicyResponse(
|
||||||
|
cleanupPolicyEntities *[]types.CleanupPolicy,
|
||||||
|
) *[]artifact.CleanupPolicy {
|
||||||
|
var cleanupPolicyDtos []artifact.CleanupPolicy
|
||||||
|
|
||||||
|
for _, value := range *cleanupPolicyEntities {
|
||||||
|
cleanupPolicy := getCleanupPolicyDto(value)
|
||||||
|
cleanupPolicyDtos = append(cleanupPolicyDtos, *cleanupPolicy)
|
||||||
|
}
|
||||||
|
return &cleanupPolicyDtos
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCleanupPolicyEntity(
|
||||||
|
cleanupPolicy artifact.CleanupPolicy,
|
||||||
|
repoID int64,
|
||||||
|
) *types.CleanupPolicy {
|
||||||
|
expireTime := time.Duration(*cleanupPolicy.ExpireDays) * 24 * time.Hour
|
||||||
|
return &types.CleanupPolicy{
|
||||||
|
Name: *cleanupPolicy.Name,
|
||||||
|
VersionPrefix: *cleanupPolicy.VersionPrefix,
|
||||||
|
PackagePrefix: *cleanupPolicy.PackagePrefix,
|
||||||
|
ExpiryTime: expireTime.Milliseconds(),
|
||||||
|
RegistryID: repoID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCleanupPolicyDto(
|
||||||
|
cleanupPolicy types.CleanupPolicy,
|
||||||
|
) *artifact.CleanupPolicy {
|
||||||
|
packagePrefix := cleanupPolicy.PackagePrefix
|
||||||
|
versionPrefix := cleanupPolicy.VersionPrefix
|
||||||
|
expiryDays := int(time.Duration(cleanupPolicy.ExpiryTime).Hours() / 24)
|
||||||
|
|
||||||
|
return &artifact.CleanupPolicy{
|
||||||
|
Name: &cleanupPolicy.Name,
|
||||||
|
VersionPrefix: &versionPrefix,
|
||||||
|
PackagePrefix: &packagePrefix,
|
||||||
|
ExpireDays: &expiryDays,
|
||||||
|
}
|
||||||
|
}
|
71
registry/app/api/controller/metadata/controller.go
Normal file
71
registry/app/api/controller/metadata/controller.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/app/auth/authz"
|
||||||
|
corestore "github.com/harness/gitness/app/store"
|
||||||
|
urlprovider "github.com/harness/gitness/app/url"
|
||||||
|
"github.com/harness/gitness/audit"
|
||||||
|
storagedriver "github.com/harness/gitness/registry/app/driver"
|
||||||
|
"github.com/harness/gitness/registry/app/store"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIController simple struct.
|
||||||
|
type APIController struct {
|
||||||
|
ArtifactStore store.ArtifactRepository
|
||||||
|
RegistryRepository store.RegistryRepository
|
||||||
|
UpstreamProxyStore store.UpstreamProxyConfigRepository
|
||||||
|
TagStore store.TagRepository
|
||||||
|
ManifestStore store.ManifestRepository
|
||||||
|
CleanupPolicyStore store.CleanupPolicyRepository
|
||||||
|
spaceStore corestore.SpaceStore
|
||||||
|
tx dbtx.Transactor
|
||||||
|
StorageDriver storagedriver.StorageDriver
|
||||||
|
URLProvider urlprovider.Provider
|
||||||
|
Authorizer authz.Authorizer
|
||||||
|
AuditService audit.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPIController(
|
||||||
|
repositoryStore store.RegistryRepository,
|
||||||
|
upstreamProxyStore store.UpstreamProxyConfigRepository,
|
||||||
|
tagStore store.TagRepository,
|
||||||
|
manifestStore store.ManifestRepository,
|
||||||
|
cleanupPolicyStore store.CleanupPolicyRepository,
|
||||||
|
artifactStore store.ArtifactRepository,
|
||||||
|
driver storagedriver.StorageDriver,
|
||||||
|
spaceStore corestore.SpaceStore,
|
||||||
|
tx dbtx.Transactor,
|
||||||
|
urlProvider urlprovider.Provider,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
auditService audit.Service,
|
||||||
|
) *APIController {
|
||||||
|
return &APIController{
|
||||||
|
RegistryRepository: repositoryStore,
|
||||||
|
UpstreamProxyStore: upstreamProxyStore,
|
||||||
|
TagStore: tagStore,
|
||||||
|
ManifestStore: manifestStore,
|
||||||
|
CleanupPolicyStore: cleanupPolicyStore,
|
||||||
|
ArtifactStore: artifactStore,
|
||||||
|
spaceStore: spaceStore,
|
||||||
|
StorageDriver: driver,
|
||||||
|
tx: tx,
|
||||||
|
URLProvider: urlProvider,
|
||||||
|
Authorizer: authorizer,
|
||||||
|
AuditService: auditService,
|
||||||
|
}
|
||||||
|
}
|
321
registry/app/api/controller/metadata/create_registry.go
Normal file
321
registry/app/api/controller/metadata/create_registry.go
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/audit"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
registrytypes "github.com/harness/gitness/registry/types"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
gitnessenum "github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) CreateRegistry(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.CreateRegistryRequestObject,
|
||||||
|
) (artifact.CreateRegistryResponseObject, error) {
|
||||||
|
registryRequest := artifact.RegistryRequest(*r.Body)
|
||||||
|
parentRef := artifact.SpaceRefPathParam(*registryRequest.ParentRef)
|
||||||
|
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, string(parentRef), "")
|
||||||
|
if err != nil {
|
||||||
|
return artifact.CreateRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.CreateRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
if err = apiauth.CheckSpaceScope(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
space,
|
||||||
|
gitnessenum.ResourceTypeRegistry,
|
||||||
|
gitnessenum.PermissionRegistryEdit,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.CreateRegistry403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if registryRequest.Config.Type == artifact.RegistryTypeVIRTUAL {
|
||||||
|
return c.createVirtualRegistry(ctx, registryRequest, regInfo, session, parentRef)
|
||||||
|
}
|
||||||
|
registry, upstreamproxy, err := CreateUpstreamProxyEntity(
|
||||||
|
registryRequest,
|
||||||
|
regInfo.parentID, regInfo.rootIdentifierID,
|
||||||
|
)
|
||||||
|
var registryID int64
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.tx.WithTx(
|
||||||
|
ctx, func(ctx context.Context) error {
|
||||||
|
registryID, err = c.createRegistryWithAudit(ctx, registry, session.Principal, string(parentRef))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create registry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
upstreamproxy.RegistryID = registryID
|
||||||
|
|
||||||
|
_, err = c.createUpstreamProxyWithAudit(
|
||||||
|
ctx, upstreamproxy, session.Principal, string(parentRef), registry.Name,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create upstream proxy: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), err
|
||||||
|
}
|
||||||
|
upstreamproxyEntity, err := c.UpstreamProxyStore.Get(ctx, registryID)
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact.CreateRegistry201JSONResponse{
|
||||||
|
RegistryResponseJSONResponse: *CreateUpstreamProxyResponseJSONResponse(upstreamproxyEntity),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) createVirtualRegistry(
|
||||||
|
ctx context.Context, registryRequest artifact.RegistryRequest, regInfo *RegistryRequestBaseInfo,
|
||||||
|
session *auth.Session, parentRef artifact.SpaceRefPathParam,
|
||||||
|
) (artifact.CreateRegistryResponseObject, error) {
|
||||||
|
registry, err := CreateRegistryEntity(registryRequest, regInfo.parentID, regInfo.rootIdentifierID)
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), nil
|
||||||
|
}
|
||||||
|
err = c.setUpstreamProxyIDs(ctx, registry, registryRequest, regInfo.parentID)
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), nil
|
||||||
|
}
|
||||||
|
id, err := c.createRegistryWithAudit(ctx, registry, session.Principal, string(parentRef))
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), nil
|
||||||
|
}
|
||||||
|
repoEntity, err := c.RegistryRepository.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), nil
|
||||||
|
}
|
||||||
|
cleanupPolicies, err := c.CleanupPolicyStore.GetByRegistryID(ctx, repoEntity.ID)
|
||||||
|
if err != nil {
|
||||||
|
return throwCreateRegistry400Error(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact.CreateRegistry201JSONResponse{
|
||||||
|
RegistryResponseJSONResponse: *CreateVirtualRepositoryResponse(
|
||||||
|
repoEntity, c.getUpstreamProxyKeys(ctx, repoEntity.UpstreamProxies),
|
||||||
|
cleanupPolicies, regInfo.rootIdentifier, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) createUpstreamProxyWithAudit(
|
||||||
|
ctx context.Context,
|
||||||
|
upstreamProxy *registrytypes.UpstreamProxyConfig, principal types.Principal,
|
||||||
|
parentRef string, registryName string,
|
||||||
|
) (int64, error) {
|
||||||
|
id, err := c.UpstreamProxyStore.Create(ctx, upstreamProxy)
|
||||||
|
if err != nil {
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
auditErr := c.AuditService.Log(
|
||||||
|
ctx,
|
||||||
|
principal,
|
||||||
|
audit.NewResource(audit.ResourceTypeRegistryUpstreamProxy, registryName),
|
||||||
|
audit.ActionCreated,
|
||||||
|
parentRef,
|
||||||
|
audit.WithNewObject(
|
||||||
|
audit.RegistryUpstreamProxyConfigObject{
|
||||||
|
ID: id,
|
||||||
|
RegistryID: upstreamProxy.RegistryID,
|
||||||
|
Source: upstreamProxy.Source,
|
||||||
|
URL: upstreamProxy.URL,
|
||||||
|
AuthType: upstreamProxy.AuthType,
|
||||||
|
CreatedAt: upstreamProxy.CreatedAt,
|
||||||
|
UpdatedAt: upstreamProxy.UpdatedAt,
|
||||||
|
CreatedBy: upstreamProxy.CreatedBy,
|
||||||
|
UpdatedBy: upstreamProxy.UpdatedBy,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if auditErr != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf(
|
||||||
|
"failed to insert audit log for create upstream proxy config operation: %s", auditErr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) createRegistryWithAudit(
|
||||||
|
ctx context.Context, registry *registrytypes.Registry,
|
||||||
|
principal types.Principal, parentRef string,
|
||||||
|
) (int64, error) {
|
||||||
|
id, err := c.RegistryRepository.Create(ctx, registry)
|
||||||
|
if err != nil {
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
auditErr := c.AuditService.Log(
|
||||||
|
ctx,
|
||||||
|
principal,
|
||||||
|
audit.NewResource(audit.ResourceTypeRegistry, registry.Name),
|
||||||
|
audit.ActionCreated,
|
||||||
|
parentRef,
|
||||||
|
audit.WithNewObject(
|
||||||
|
audit.RegistryObject{
|
||||||
|
Registry: *registry,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if auditErr != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("failed to insert audit log for create registry operation: %s", auditErr)
|
||||||
|
}
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func throwCreateRegistry400Error(err error) artifact.CreateRegistry400JSONResponse {
|
||||||
|
return artifact.CreateRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRegistryEntity(
|
||||||
|
dto artifact.RegistryRequest, parentID int64,
|
||||||
|
rootParentID int64,
|
||||||
|
) (*registrytypes.Registry, error) {
|
||||||
|
allowedPattern, blockedPattern, description, labels := getRepoEntityFields(dto)
|
||||||
|
e := ValidatePackageType(string(dto.PackageType))
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
e = ValidateRepoType(string(dto.Config.Type))
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
e = ValidateIdentifier(dto.Identifier)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
entity := ®istrytypes.Registry{
|
||||||
|
Name: dto.Identifier,
|
||||||
|
ParentID: parentID,
|
||||||
|
RootParentID: rootParentID,
|
||||||
|
Description: description,
|
||||||
|
AllowedPattern: allowedPattern,
|
||||||
|
BlockedPattern: blockedPattern,
|
||||||
|
PackageType: dto.PackageType,
|
||||||
|
Labels: labels,
|
||||||
|
Type: dto.Config.Type,
|
||||||
|
}
|
||||||
|
return entity, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUpstreamProxyEntity(
|
||||||
|
dto artifact.RegistryRequest,
|
||||||
|
parentID int64,
|
||||||
|
rootParentID int64,
|
||||||
|
) (*registrytypes.Registry, *registrytypes.UpstreamProxyConfig, error) {
|
||||||
|
allowedPattern := []string{}
|
||||||
|
if dto.AllowedPattern != nil {
|
||||||
|
allowedPattern = *dto.AllowedPattern
|
||||||
|
}
|
||||||
|
blockedPattern := []string{}
|
||||||
|
if dto.BlockedPattern != nil {
|
||||||
|
blockedPattern = *dto.BlockedPattern
|
||||||
|
}
|
||||||
|
e := ValidatePackageType(string(dto.PackageType))
|
||||||
|
if e != nil {
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
e = ValidateUpstream(dto.Config)
|
||||||
|
if e != nil {
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
e = ValidateIdentifier(dto.Identifier)
|
||||||
|
if e != nil {
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
repoEntity := ®istrytypes.Registry{
|
||||||
|
Name: dto.Identifier,
|
||||||
|
ParentID: parentID,
|
||||||
|
RootParentID: rootParentID,
|
||||||
|
AllowedPattern: allowedPattern,
|
||||||
|
BlockedPattern: blockedPattern,
|
||||||
|
PackageType: dto.PackageType,
|
||||||
|
Type: artifact.RegistryTypeUPSTREAM,
|
||||||
|
}
|
||||||
|
|
||||||
|
config, e := dto.Config.AsUpstreamConfig()
|
||||||
|
if e != nil {
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
CleanURLPath(config.Url)
|
||||||
|
upstreamProxyConfigEntity := ®istrytypes.UpstreamProxyConfig{
|
||||||
|
URL: *config.Url,
|
||||||
|
AuthType: string(config.AuthType),
|
||||||
|
}
|
||||||
|
if config.Source != nil && len(string(*config.Source)) > 0 {
|
||||||
|
err := ValidateUpstreamSource(string(*config.Source))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
upstreamProxyConfigEntity.Source = string(*config.Source)
|
||||||
|
}
|
||||||
|
if config.AuthType == artifact.AuthTypeUserPassword {
|
||||||
|
res, err := config.Auth.AsUserPassword()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
upstreamProxyConfigEntity.UserName = res.UserName
|
||||||
|
if res.SecretIdentifier == nil || res.SecretSpaceId == nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to create upstream proxy: secret_identifier or secret_space_id missing")
|
||||||
|
}
|
||||||
|
upstreamProxyConfigEntity.SecretIdentifier = *res.SecretIdentifier
|
||||||
|
upstreamProxyConfigEntity.SecretSpaceID = *res.SecretSpaceId
|
||||||
|
}
|
||||||
|
return repoEntity, upstreamProxyConfigEntity, nil
|
||||||
|
}
|
170
registry/app/api/controller/metadata/delete_registry.go
Normal file
170
registry/app/api/controller/metadata/delete_registry.go
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/audit"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
registrytypes "github.com/harness/gitness/registry/types"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) DeleteRegistry(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.DeleteRegistryRequestObject,
|
||||||
|
) (artifact.DeleteRegistryResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.DeleteRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.DeleteRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryDelete)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.DeleteRegistry403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoEntity, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if len(repoEntity.Name) == 0 {
|
||||||
|
return artifact.DeleteRegistry404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "registry doesn't exist with this key"),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return throwDeleteRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(repoEntity.Type) == string(artifact.RegistryTypeVIRTUAL) {
|
||||||
|
err = c.deleteRegistryWithAudit(ctx, regInfo, repoEntity, session.Principal, regInfo.parentRef)
|
||||||
|
} else {
|
||||||
|
err = c.tx.WithTx(
|
||||||
|
ctx, func(ctx context.Context) error {
|
||||||
|
err = c.deleteUpstreamProxyWithAudit(
|
||||||
|
ctx, regInfo, session.Principal, regInfo.parentRef, repoEntity.Name,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete upstream proxy: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.deleteRegistryWithAudit(ctx, regInfo, repoEntity, session.Principal, regInfo.parentRef)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete registry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return throwDeleteRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
return artifact.DeleteRegistry200JSONResponse{
|
||||||
|
SuccessJSONResponse: artifact.SuccessJSONResponse(*GetSuccessResponse()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) deleteUpstreamProxyWithAudit(
|
||||||
|
ctx context.Context,
|
||||||
|
regInfo *RegistryRequestBaseInfo, principal types.Principal, parentRef string, registryName string,
|
||||||
|
) error {
|
||||||
|
err := c.UpstreamProxyStore.Delete(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
auditErr := c.AuditService.Log(
|
||||||
|
ctx,
|
||||||
|
principal,
|
||||||
|
audit.NewResource(audit.ResourceTypeRegistryUpstreamProxy, registryName),
|
||||||
|
audit.ActionDeleted,
|
||||||
|
parentRef,
|
||||||
|
audit.WithData("registry name", registryName),
|
||||||
|
)
|
||||||
|
if auditErr != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf(
|
||||||
|
"failed to insert audit log for delete upstream proxy config operation: %s", auditErr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) deleteRegistryWithAudit(
|
||||||
|
ctx context.Context, regInfo *RegistryRequestBaseInfo,
|
||||||
|
registry *registrytypes.Registry, principal types.Principal, parentRef string,
|
||||||
|
) error {
|
||||||
|
err := c.RegistryRepository.Delete(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
auditErr := c.AuditService.Log(
|
||||||
|
ctx,
|
||||||
|
principal,
|
||||||
|
audit.NewResource(audit.ResourceTypeRegistry, registry.Name),
|
||||||
|
audit.ActionDeleted,
|
||||||
|
parentRef,
|
||||||
|
audit.WithOldObject(
|
||||||
|
audit.RegistryObject{
|
||||||
|
Registry: *registry,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if auditErr != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("failed to insert audit log for delete registry operation: %s", auditErr)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func throwDeleteRegistry500Error(err error) artifact.DeleteRegistry500JSONResponse {
|
||||||
|
return artifact.DeleteRegistry500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
112
registry/app/api/controller/metadata/get_artifact_stats.go
Normal file
112
registry/app/api/controller/metadata/get_artifact_stats.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetArtifactStats(
|
||||||
|
_ context.Context,
|
||||||
|
_ artifact.GetArtifactStatsRequestObject,
|
||||||
|
) (artifact.GetArtifactStatsResponseObject, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) GetArtifactStatsForSpace(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetArtifactStatsForSpaceRequestObject,
|
||||||
|
) (artifact.GetArtifactStatsForSpaceResponseObject, error) {
|
||||||
|
parentRef := r.SpaceRef
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, string(parentRef), "")
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactStatsForSpace400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactStatsForSpace400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetArtifactStatsForSpace403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) GetArtifactStatsForRegistry(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetArtifactStatsForRegistryRequestObject,
|
||||||
|
) (artifact.GetArtifactStatsForRegistryResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactStatsForRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactStatsForRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
if err = apiauth.CheckSpaceScope(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
space,
|
||||||
|
enum.ResourceTypeRegistry,
|
||||||
|
enum.PermissionRegistryView,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetArtifactStatsForRegistry403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
113
registry/app/api/controller/metadata/get_artifacts.go
Normal file
113
registry/app/api/controller/metadata/get_artifacts.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetAllArtifacts(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetAllArtifactsRequestObject,
|
||||||
|
) (artifact.GetAllArtifactsResponseObject, error) {
|
||||||
|
ref := ""
|
||||||
|
if r.Params.RegIdentifier != nil {
|
||||||
|
ref2, err2 := GetRegRef(string(r.SpaceRef), string(*r.Params.RegIdentifier))
|
||||||
|
if err2 != nil {
|
||||||
|
return c.getAllArtifacts400JsonResponse(err2)
|
||||||
|
}
|
||||||
|
ref = ref2
|
||||||
|
}
|
||||||
|
|
||||||
|
regInfo, err := c.GetRegistryRequestInfo(
|
||||||
|
ctx, r.Params.PackageType, r.Params.Page, r.Params.Size,
|
||||||
|
r.Params.SearchTerm, ArtifactResource, string(r.SpaceRef), ref, r.Params.Label,
|
||||||
|
r.Params.SortOrder, r.Params.SortField,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return c.getAllArtifacts400JsonResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetAllArtifacts400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetAllArtifacts403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var artifacts *[]types.ArtifactMetadata
|
||||||
|
var count int64
|
||||||
|
if len(regInfo.RegistryIdentifier) == 0 {
|
||||||
|
artifacts, err = c.TagStore.GetAllArtifactsByParentID(
|
||||||
|
ctx, regInfo.parentID, ®Info.packageTypes,
|
||||||
|
regInfo.sortByField, regInfo.sortByOrder, regInfo.limit, regInfo.offset, regInfo.searchTerm, regInfo.labels,
|
||||||
|
)
|
||||||
|
count, _ = c.TagStore.CountAllArtifactsByParentID(
|
||||||
|
ctx, regInfo.parentID, ®Info.packageTypes,
|
||||||
|
regInfo.searchTerm, regInfo.labels,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
artifacts, err = c.TagStore.GetAllArtifactsByRepo(
|
||||||
|
ctx, regInfo.parentID, regInfo.RegistryIdentifier,
|
||||||
|
regInfo.sortByField, regInfo.sortByOrder, regInfo.limit, regInfo.offset, regInfo.searchTerm, regInfo.labels,
|
||||||
|
)
|
||||||
|
count, _ = c.TagStore.CountAllArtifactsByRepo(
|
||||||
|
ctx, regInfo.parentID, regInfo.RegistryIdentifier,
|
||||||
|
regInfo.searchTerm, regInfo.labels,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetAllArtifacts500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return artifact.GetAllArtifacts200JSONResponse{
|
||||||
|
ListArtifactResponseJSONResponse: *GetAllArtifactResponse(artifacts, count, regInfo.pageNumber, regInfo.limit),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) getAllArtifacts400JsonResponse(err error) (artifact.GetAllArtifactsResponseObject, error) {
|
||||||
|
return artifact.GetAllArtifacts400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
store2 "github.com/harness/gitness/store"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetDockerArtifactDetails(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetDockerArtifactDetailsRequestObject,
|
||||||
|
) (artifact.GetDockerArtifactDetailsResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactDetails400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactDetails400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetDockerArtifactDetails403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
image := string(r.Artifact)
|
||||||
|
version := string(r.Version)
|
||||||
|
manifestDigest := string(r.Params.Digest)
|
||||||
|
|
||||||
|
registry, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactDetails500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := c.TagStore.GetTagDetail(ctx, registry.ID, image, version)
|
||||||
|
if err != nil {
|
||||||
|
return getArtifactDetailsErrResponse(err)
|
||||||
|
}
|
||||||
|
dgst, err := types.NewDigest(digest.Digest(manifestDigest))
|
||||||
|
if err != nil {
|
||||||
|
return getArtifactDetailsErrResponse(err)
|
||||||
|
}
|
||||||
|
m, err := c.ManifestStore.FindManifestByDigest(ctx, registry.ID, image, dgst)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, store2.ErrResourceNotFound) {
|
||||||
|
return getArtifactDetailsErrResponse(fmt.Errorf("manifest not found"))
|
||||||
|
}
|
||||||
|
return getArtifactDetailsErrResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
latestTag, err := c.TagStore.GetLatestTag(ctx, registry.ID, image)
|
||||||
|
if err != nil {
|
||||||
|
return getArtifactDetailsErrResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact.GetDockerArtifactDetails200JSONResponse{
|
||||||
|
DockerArtifactDetailResponseJSONResponse: *GetDockerArtifactDetails(
|
||||||
|
registry, tag, m,
|
||||||
|
latestTag.ID == tag.ID, regInfo, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArtifactDetailsErrResponse(err error) (artifact.GetDockerArtifactDetailsResponseObject, error) {
|
||||||
|
return artifact.GetDockerArtifactDetails500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
store2 "github.com/harness/gitness/store"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetDockerArtifactLayers(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetDockerArtifactLayersRequestObject,
|
||||||
|
) (artifact.GetDockerArtifactLayersResponseObject, error) {
|
||||||
|
regInfo, _ := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactLayers400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetDockerArtifactLayers403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestDigest := string(r.Params.Digest)
|
||||||
|
image := string(r.Artifact)
|
||||||
|
|
||||||
|
dgst, err := types.NewDigest(digest.Digest(manifestDigest))
|
||||||
|
if err != nil {
|
||||||
|
return getLayersErrorResponse(err)
|
||||||
|
}
|
||||||
|
registry, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return getLayersErrorResponse(err)
|
||||||
|
}
|
||||||
|
if registry == nil {
|
||||||
|
return getLayersErrorResponse(fmt.Errorf("repository not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := c.ManifestStore.FindManifestByDigest(ctx, registry.ID, image, dgst)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, store2.ErrResourceNotFound) {
|
||||||
|
return getLayersErrorResponse(fmt.Errorf("manifest not found"))
|
||||||
|
}
|
||||||
|
return getLayersErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mConfig, err := getManifestConfig(ctx, m.Configuration.Digest, regInfo.rootIdentifier, c.StorageDriver)
|
||||||
|
if err != nil {
|
||||||
|
return getLayersErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
layersSummary := &artifact.DockerLayersSummary{
|
||||||
|
Digest: m.Digest.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if mConfig != nil {
|
||||||
|
osArch := fmt.Sprintf("%s/%s", mConfig.Os, mConfig.Arch)
|
||||||
|
layersSummary.OsArch = &osArch
|
||||||
|
var historyLayers []artifact.DockerLayerEntry
|
||||||
|
for _, history := range mConfig.History {
|
||||||
|
historyLayers = append(
|
||||||
|
historyLayers, artifact.DockerLayerEntry{
|
||||||
|
Command: history.CreatedBy,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
layersSummary.Layers = &historyLayers
|
||||||
|
} else {
|
||||||
|
return getLayersErrorResponse(fmt.Errorf("manifest config not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact.GetDockerArtifactLayers200JSONResponse{
|
||||||
|
DockerLayersResponseJSONResponse: artifact.DockerLayersResponseJSONResponse{
|
||||||
|
Data: *layersSummary,
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLayersErrorResponse(err error) (artifact.GetDockerArtifactLayersResponseObject, error) {
|
||||||
|
return artifact.GetDockerArtifactLayers500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetDockerArtifactManifest(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetDockerArtifactManifestRequestObject,
|
||||||
|
) (artifact.GetDockerArtifactManifestResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactManifest400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactManifest400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetDockerArtifactManifest403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
imageName := string(r.Artifact)
|
||||||
|
dgst := string(r.Params.Digest)
|
||||||
|
manifestDigest, err := types.NewDigest(digest.Digest(dgst))
|
||||||
|
if err != nil {
|
||||||
|
return getArtifactManifestErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestPayload, err := c.ManifestStore.GetManifestPayload(
|
||||||
|
ctx,
|
||||||
|
regInfo.parentID,
|
||||||
|
regInfo.RegistryIdentifier,
|
||||||
|
imageName,
|
||||||
|
manifestDigest,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return getArtifactManifestErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := *manifestPayload
|
||||||
|
return artifact.GetDockerArtifactManifest200JSONResponse{
|
||||||
|
DockerArtifactManifestResponseJSONResponse: artifact.DockerArtifactManifestResponseJSONResponse{
|
||||||
|
Data: artifact.DockerArtifactManifest{
|
||||||
|
Manifest: string(payload),
|
||||||
|
},
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArtifactManifestErrorResponse(err error) (artifact.GetDockerArtifactManifestResponseObject, error) {
|
||||||
|
return artifact.GetDockerArtifactManifest500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
ml "github.com/harness/gitness/registry/app/manifest/manifestlist"
|
||||||
|
os "github.com/harness/gitness/registry/app/manifest/ocischema"
|
||||||
|
s2 "github.com/harness/gitness/registry/app/manifest/schema2"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
store2 "github.com/harness/gitness/store"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetDockerArtifactManifests(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetDockerArtifactManifestsRequestObject,
|
||||||
|
) (artifact.GetDockerArtifactManifestsResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactManifests400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetDockerArtifactManifests400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetDockerArtifactManifests403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
image := string(r.Artifact)
|
||||||
|
version := string(r.Version)
|
||||||
|
registry, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t, err := c.TagStore.FindTag(ctx, registry.ID, image, version)
|
||||||
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m, err := c.ManifestStore.Get(ctx, t.ManifestID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
manifest, err := docker.DBManifestToManifest(m)
|
||||||
|
manifestDetailsList := []artifact.DockerManifestDetails{}
|
||||||
|
switch reqManifest := manifest.(type) {
|
||||||
|
case *s2.DeserializedManifest:
|
||||||
|
mConfig, err := getManifestConfig(ctx, reqManifest.Config().Digest, regInfo.rootIdentifier, c.StorageDriver)
|
||||||
|
if err != nil {
|
||||||
|
return artifactManifestsErrorRs(err), nil
|
||||||
|
}
|
||||||
|
manifestDetailsList = append(manifestDetailsList, getManifestDetails(m, mConfig))
|
||||||
|
case *os.DeserializedManifest:
|
||||||
|
mConfig, err := getManifestConfig(ctx, reqManifest.Config().Digest, regInfo.rootIdentifier, c.StorageDriver)
|
||||||
|
if err != nil {
|
||||||
|
return artifactManifestsErrorRs(err), nil
|
||||||
|
}
|
||||||
|
manifestDetailsList = append(manifestDetailsList, getManifestDetails(m, mConfig))
|
||||||
|
case *ml.DeserializedManifestList:
|
||||||
|
for _, manifestEntry := range reqManifest.Manifests {
|
||||||
|
dgst, err := types.NewDigest(manifestEntry.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return artifactManifestsErrorRs(err), nil
|
||||||
|
}
|
||||||
|
referencedManifest, err := c.ManifestStore.FindManifestByDigest(ctx, registry.ID, image, dgst)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, store2.ErrResourceNotFound) {
|
||||||
|
return artifactManifestsErrorRs(
|
||||||
|
fmt.Errorf("manifest not found"),
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
return artifactManifestsErrorRs(err), nil
|
||||||
|
}
|
||||||
|
mConfig, err := getManifestConfig(
|
||||||
|
ctx, referencedManifest.Configuration.Digest,
|
||||||
|
regInfo.rootIdentifier, c.StorageDriver,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return artifactManifestsErrorRs(err), nil
|
||||||
|
}
|
||||||
|
manifestDetailsList = append(manifestDetailsList, getManifestDetails(referencedManifest, mConfig))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Ctx(ctx).Error().Stack().Err(err).Msgf("Unknown manifest type: %T", manifest)
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact.GetDockerArtifactManifests200JSONResponse{
|
||||||
|
DockerManifestsResponseJSONResponse: artifact.DockerManifestsResponseJSONResponse{
|
||||||
|
Data: artifact.DockerManifests{
|
||||||
|
ImageName: t.ImageName,
|
||||||
|
Version: t.Name,
|
||||||
|
Manifests: &manifestDetailsList,
|
||||||
|
},
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func artifactManifestsErrorRs(err error) artifact.GetDockerArtifactManifestsResponseObject {
|
||||||
|
return artifact.GetDockerArtifactManifests500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getManifestDetails(m *types.Manifest, mConfig *manifestConfig) artifact.DockerManifestDetails {
|
||||||
|
createdAt := GetTimeInMs(m.CreatedAt)
|
||||||
|
size := GetSize(m.TotalSize)
|
||||||
|
|
||||||
|
manifestDetails := artifact.DockerManifestDetails{
|
||||||
|
Digest: m.Digest.String(),
|
||||||
|
CreatedAt: &createdAt,
|
||||||
|
Size: &size,
|
||||||
|
}
|
||||||
|
if mConfig != nil {
|
||||||
|
manifestDetails.OsArch = fmt.Sprintf("%s/%s", mConfig.Os, mConfig.Arch)
|
||||||
|
}
|
||||||
|
return manifestDetails
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
store2 "github.com/harness/gitness/store"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetHelmArtifactDetails(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetHelmArtifactDetailsRequestObject,
|
||||||
|
) (artifact.GetHelmArtifactDetailsResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetHelmArtifactDetails400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetHelmArtifactDetails400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetHelmArtifactDetails403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
image := string(r.Artifact)
|
||||||
|
version := string(r.Version)
|
||||||
|
|
||||||
|
registry, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetHelmArtifactDetails500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := c.TagStore.GetTagDetail(ctx, registry.ID, image, version)
|
||||||
|
if err != nil {
|
||||||
|
return getHelmArtifactDetailsErrResponse(err)
|
||||||
|
}
|
||||||
|
m, err := c.ManifestStore.FindManifestByTagName(ctx, registry.ID, image, version)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, store2.ErrResourceNotFound) {
|
||||||
|
return getHelmArtifactDetailsErrResponse(fmt.Errorf("manifest not found"))
|
||||||
|
}
|
||||||
|
return getHelmArtifactDetailsErrResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
latestTag, _ := c.TagStore.GetLatestTag(ctx, registry.ID, image)
|
||||||
|
|
||||||
|
return artifact.GetHelmArtifactDetails200JSONResponse{
|
||||||
|
HelmArtifactDetailResponseJSONResponse: *GetHelmArtifactDetails(
|
||||||
|
registry, tag, m,
|
||||||
|
latestTag.ID == tag.ID, regInfo.rootIdentifier, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHelmArtifactDetailsErrResponse(err error) (artifact.GetHelmArtifactDetailsResponseObject, error) {
|
||||||
|
return artifact.GetHelmArtifactDetails500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
store2 "github.com/harness/gitness/store"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetHelmArtifactManifest(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetHelmArtifactManifestRequestObject,
|
||||||
|
) (artifact.GetHelmArtifactManifestResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return c.get400Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetHelmArtifactManifest400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetHelmArtifactManifest403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
imageName := string(r.Artifact)
|
||||||
|
version := string(r.Version)
|
||||||
|
|
||||||
|
manifestPayload, err := c.ManifestStore.FindManifestPayloadByTagName(
|
||||||
|
ctx,
|
||||||
|
regInfo.parentID,
|
||||||
|
regInfo.RegistryIdentifier,
|
||||||
|
imageName,
|
||||||
|
version,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, store2.ErrResourceNotFound) {
|
||||||
|
return artifact.GetHelmArtifactManifest400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return artifact.GetHelmArtifactManifest500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := *manifestPayload
|
||||||
|
return artifact.GetHelmArtifactManifest200JSONResponse{
|
||||||
|
HelmArtifactManifestResponseJSONResponse: artifact.HelmArtifactManifestResponseJSONResponse{
|
||||||
|
Data: artifact.HelmArtifactManifest{
|
||||||
|
Manifest: string(payload),
|
||||||
|
},
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) get400Error(err error) (artifact.GetHelmArtifactManifestResponseObject, error) {
|
||||||
|
return artifact.GetHelmArtifactManifest400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
83
registry/app/api/controller/metadata/get_artifacts_labels.go
Normal file
83
registry/app/api/controller/metadata/get_artifacts_labels.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) ListArtifactLabels(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.ListArtifactLabelsRequestObject,
|
||||||
|
) (artifact.ListArtifactLabelsResponseObject, error) {
|
||||||
|
regInfo, _ := c.GetRegistryRequestInfo(
|
||||||
|
ctx, nil, r.Params.Page, r.Params.Size,
|
||||||
|
r.Params.SearchTerm, ArtifactResource, "", string(r.RegistryRef),
|
||||||
|
nil, nil, nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.ListArtifactLabels400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.ListArtifactLabels403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
labels, err := c.ArtifactStore.GetLabelsByParentIDAndRepo(
|
||||||
|
ctx, regInfo.parentID,
|
||||||
|
regInfo.RegistryIdentifier, regInfo.limit, regInfo.offset, regInfo.searchTerm,
|
||||||
|
)
|
||||||
|
count, _ := c.ArtifactStore.CountLabelsByParentIDAndRepo(
|
||||||
|
ctx, regInfo.parentID,
|
||||||
|
regInfo.RegistryIdentifier, regInfo.searchTerm,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return artifact.ListArtifactLabels500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return artifact.ListArtifactLabels200JSONResponse{
|
||||||
|
ListArtifactLabelResponseJSONResponse: *GetAllArtifactLabelsResponse(
|
||||||
|
&labels, count,
|
||||||
|
regInfo.pageNumber, regInfo.limit,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetArtifactSummary(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetArtifactSummaryRequestObject,
|
||||||
|
) (artifact.GetArtifactSummaryResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactSummary400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactSummary400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetArtifactSummary403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
image := string(r.Artifact)
|
||||||
|
|
||||||
|
tag, err := c.TagStore.GetLatestTagMetadata(ctx, regInfo.parentID, regInfo.RegistryIdentifier, image)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactSummary500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return artifact.GetArtifactSummary200JSONResponse{
|
||||||
|
ArtifactSummaryResponseJSONResponse: *GetArtifactSummary(*tag),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetArtifactVersionSummary(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetArtifactVersionSummaryRequestObject,
|
||||||
|
) (artifact.GetArtifactVersionSummaryResponseObject, error) {
|
||||||
|
regInfo, _ := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactVersionSummary400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetArtifactVersionSummary403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
image := string(r.Artifact)
|
||||||
|
version := string(r.Version)
|
||||||
|
|
||||||
|
tag, err := c.TagStore.GetTagMetadata(ctx, regInfo.parentID, regInfo.RegistryIdentifier, image, version)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetArtifactVersionSummary500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
latestTag, _ := c.TagStore.GetLatestTagName(ctx, regInfo.parentID, regInfo.RegistryIdentifier, image)
|
||||||
|
|
||||||
|
isLatestTag := latestTag == version
|
||||||
|
|
||||||
|
return artifact.GetArtifactVersionSummary200JSONResponse{
|
||||||
|
ArtifactVersionSummaryResponseJSONResponse: *GetArtifactVersionSummary(tag, image, isLatestTag),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetAllArtifactVersions(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetAllArtifactVersionsRequestObject,
|
||||||
|
) (artifact.GetAllArtifactVersionsResponseObject, error) {
|
||||||
|
regInfo, _ := c.GetRegistryRequestInfo(
|
||||||
|
ctx, nil, r.Params.Page, r.Params.Size,
|
||||||
|
r.Params.SearchTerm, ArtifactVersionResource, "", string(r.RegistryRef),
|
||||||
|
nil, r.Params.SortOrder, r.Params.SortField,
|
||||||
|
)
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetAllArtifactVersions400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetAllArtifactVersions403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
image := string(r.Artifact)
|
||||||
|
|
||||||
|
tags, err := c.TagStore.GetAllTagsByRepoAndImage(
|
||||||
|
ctx, regInfo.parentID, regInfo.RegistryIdentifier,
|
||||||
|
image, regInfo.sortByField, regInfo.sortByOrder, regInfo.limit, regInfo.offset, regInfo.searchTerm,
|
||||||
|
)
|
||||||
|
|
||||||
|
latestTag, _ := c.TagStore.GetLatestTagName(ctx, regInfo.parentID, regInfo.RegistryIdentifier, image)
|
||||||
|
|
||||||
|
count, _ := c.TagStore.CountAllTagsByRepoAndImage(
|
||||||
|
ctx, regInfo.parentID, regInfo.RegistryIdentifier,
|
||||||
|
image, regInfo.searchTerm,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetAllArtifactVersions500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact.GetAllArtifactVersions200JSONResponse{
|
||||||
|
ListArtifactVersionResponseJSONResponse: *GetAllArtifactVersionResponse(
|
||||||
|
ctx, tags, latestTag, image, count,
|
||||||
|
regInfo, regInfo.pageNumber, regInfo.limit, regInfo.rootIdentifier, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
317
registry/app/api/controller/metadata/get_client_setup_details.go
Normal file
317
registry/app/api/controller/metadata/get_client_setup_details.go
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/app/common"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetClientSetupDetails(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetClientSetupDetailsRequestObject,
|
||||||
|
) (artifact.GetClientSetupDetailsResponseObject, error) {
|
||||||
|
regRefParam := r.RegistryRef
|
||||||
|
imageParam := r.Params.Artifact
|
||||||
|
tagParam := r.Params.Version
|
||||||
|
|
||||||
|
regInfo, _ := c.GetRegistryRequestBaseInfo(ctx, "", string(regRefParam))
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetClientSetupDetails400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetClientSetupDetails403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetClientSetupDetails404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "registry doesn't exist with this ref"),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if imageParam != nil {
|
||||||
|
_, err := c.ArtifactStore.GetByName(ctx, reg.ID, string(*imageParam))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetClientSetupDetails404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "image doesn't exist"),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
if tagParam != nil {
|
||||||
|
_, err := c.TagStore.FindTag(ctx, reg.ID, string(*imageParam), string(*tagParam))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetClientSetupDetails404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "tag doesn't exist"),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packageType := string(reg.PackageType)
|
||||||
|
|
||||||
|
return artifact.GetClientSetupDetails200JSONResponse{
|
||||||
|
ClientSetupDetailsResponseJSONResponse: *GetClientSetupDetails(
|
||||||
|
ctx, packageType, regInfo, reg,
|
||||||
|
string(r.RegistryRef), imageParam, tagParam, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClientSetupDetails(
|
||||||
|
ctx context.Context,
|
||||||
|
packageType string,
|
||||||
|
_ *RegistryRequestBaseInfo,
|
||||||
|
_ *types.Registry,
|
||||||
|
regRef string,
|
||||||
|
image *artifact.ArtifactParam,
|
||||||
|
tag *artifact.VersionParam,
|
||||||
|
registryURL string,
|
||||||
|
) *artifact.ClientSetupDetailsResponseJSONResponse {
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
username := session.Principal.Email
|
||||||
|
hostname := common.GenerateSetupClientHostname(registryURL)
|
||||||
|
regRef = strings.ToLower(regRef)
|
||||||
|
|
||||||
|
// Fixme: Use ENUMS
|
||||||
|
if packageType == "HELM" {
|
||||||
|
header1 := "Login to Helm"
|
||||||
|
section1step1Header := "Run this Helm command in your terminal to authenticate the client."
|
||||||
|
section1step1Commands := []string{"helm registry login <HOSTNAME>"}
|
||||||
|
section1step1Type := artifact.ClientSetupStepTypeStatic
|
||||||
|
section1step2Header := "For the Password field above, generate an identity token"
|
||||||
|
section1step2Type := artifact.ClientSetupStepTypeGenerateToken
|
||||||
|
section1 := []artifact.ClientSetupStep{
|
||||||
|
{
|
||||||
|
Header: §ion1step1Header,
|
||||||
|
Commands: §ion1step1Commands,
|
||||||
|
Type: §ion1step1Type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: §ion1step2Header,
|
||||||
|
Type: §ion1step2Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
header2 := "Push a version"
|
||||||
|
section2step1Header := "Run this Helm push command in your terminal to push a chart in OCI form." +
|
||||||
|
" Note: Make sure you add oci:// prefix to the repository URL."
|
||||||
|
section2step1Commands := []string{"helm push <CHART_TGZ_FILE> oci://<HOSTNAME>/<REPOSITORY_REFERENCE>"}
|
||||||
|
section2step1Type := artifact.ClientSetupStepTypeStatic
|
||||||
|
section2 := []artifact.ClientSetupStep{
|
||||||
|
{
|
||||||
|
Header: §ion2step1Header,
|
||||||
|
Commands: §ion2step1Commands,
|
||||||
|
Type: §ion2step1Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
header3 := "Pull a version"
|
||||||
|
section3step1Header := "Run this Helm command in your terminal to pull a specific chart version."
|
||||||
|
section3step1Commands := []string{
|
||||||
|
"helm pull oci://<HOSTNAME>/<REPOSITORY_REFERENCE>/<IMAGE_NAME> --version <TAG>",
|
||||||
|
}
|
||||||
|
section3step1Type := artifact.ClientSetupStepTypeStatic
|
||||||
|
section3 := []artifact.ClientSetupStep{
|
||||||
|
{
|
||||||
|
Header: §ion3step1Header,
|
||||||
|
Commands: §ion3step1Commands,
|
||||||
|
Type: §ion3step1Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
clientSetupDetails := artifact.ClientSetupDetails{
|
||||||
|
MainHeader: "Helm Client Setup",
|
||||||
|
SecHeader: "Follow these instructions to install/use Helm artifacts or compatible packages.",
|
||||||
|
Sections: []artifact.ClientSetupSection{
|
||||||
|
{
|
||||||
|
Header: &header1,
|
||||||
|
Steps: §ion1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: &header2,
|
||||||
|
Steps: §ion2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: &header3,
|
||||||
|
Steps: §ion3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
replacePlaceholders(clientSetupDetails, username, hostname, regRef, image, tag)
|
||||||
|
|
||||||
|
return &artifact.ClientSetupDetailsResponseJSONResponse{
|
||||||
|
Data: clientSetupDetails,
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header1 := "Login to Docker"
|
||||||
|
section1step1Header := "Run this Docker command in your terminal to authenticate the client."
|
||||||
|
section1step1Commands := []string{"docker login <HOSTNAME>", "Username: <USERNAME>", "Password: *see step 2*"}
|
||||||
|
section1step1Type := artifact.ClientSetupStepTypeStatic
|
||||||
|
section1step2Header := "For the Password field above, generate an identity token"
|
||||||
|
section1step2Type := artifact.ClientSetupStepTypeGenerateToken
|
||||||
|
section1 := []artifact.ClientSetupStep{
|
||||||
|
{
|
||||||
|
Header: §ion1step1Header,
|
||||||
|
Commands: §ion1step1Commands,
|
||||||
|
Type: §ion1step1Type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: §ion1step2Header,
|
||||||
|
Type: §ion1step2Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header2 := "Pull an image"
|
||||||
|
section2step1Header := "Run this Docker command in your terminal to pull image."
|
||||||
|
section2step1Commands := []string{"docker pull <HOSTNAME>/<REPOSITORY_REFERENCE>/<IMAGE_NAME>:<TAG>"}
|
||||||
|
section2step1Type := artifact.ClientSetupStepTypeStatic
|
||||||
|
section2 := []artifact.ClientSetupStep{
|
||||||
|
{
|
||||||
|
Header: §ion2step1Header,
|
||||||
|
Commands: §ion2step1Commands,
|
||||||
|
Type: §ion2step1Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header3 := "Retag and Push the image"
|
||||||
|
section3step1Header := "Run this Docker command in your terminal to tag the image."
|
||||||
|
section3step1Commands := []string{
|
||||||
|
"docker tag <HOSTNAME>/<REPOSITORY_REFERENCE>/<IMAGE_NAME>" +
|
||||||
|
" <HOSTNAME>/<REPOSITORY_REFERENCE>/<IMAGE_NAME>:<TAG>",
|
||||||
|
}
|
||||||
|
section3step1Type := artifact.ClientSetupStepTypeStatic
|
||||||
|
section3step2Header := "Run this Docker command in your terminal to push the image."
|
||||||
|
section3step2Commands := []string{"docker push <HOSTNAME>/<REPOSITORY_REFERENCE>/<IMAGE_NAME>:<TAG>"}
|
||||||
|
section3step2Type := artifact.ClientSetupStepTypeStatic
|
||||||
|
section3 := []artifact.ClientSetupStep{
|
||||||
|
{
|
||||||
|
Header: §ion3step1Header,
|
||||||
|
Commands: §ion3step1Commands,
|
||||||
|
Type: §ion3step1Type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: §ion3step2Header,
|
||||||
|
Commands: §ion3step2Commands,
|
||||||
|
Type: §ion3step2Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
clientSetupDetails := artifact.ClientSetupDetails{
|
||||||
|
MainHeader: "Docker Client Setup",
|
||||||
|
SecHeader: "Follow these instructions to install/use Docker artifacts or compatible packages.",
|
||||||
|
Sections: []artifact.ClientSetupSection{
|
||||||
|
{
|
||||||
|
Header: &header1,
|
||||||
|
Steps: §ion1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: &header2,
|
||||||
|
Steps: §ion2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: &header3,
|
||||||
|
Steps: §ion3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
replacePlaceholders(clientSetupDetails, username, hostname, regRef, image, tag)
|
||||||
|
|
||||||
|
return &artifact.ClientSetupDetailsResponseJSONResponse{
|
||||||
|
Data: clientSetupDetails,
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func replacePlaceholders(
|
||||||
|
clientSetupDetails artifact.ClientSetupDetails, username string, hostname string,
|
||||||
|
regRef string, image *artifact.ArtifactParam, tag *artifact.VersionParam,
|
||||||
|
) {
|
||||||
|
for _, s := range clientSetupDetails.Sections {
|
||||||
|
if s.Steps == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, st := range *s.Steps {
|
||||||
|
if st.Commands == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i := range *st.Commands {
|
||||||
|
replaceText(username, st, i, hostname, regRef, image, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceText(
|
||||||
|
username string,
|
||||||
|
st artifact.ClientSetupStep,
|
||||||
|
i int,
|
||||||
|
hostname string,
|
||||||
|
regRef string,
|
||||||
|
image *artifact.ArtifactParam,
|
||||||
|
tag *artifact.VersionParam,
|
||||||
|
) {
|
||||||
|
if username != "" {
|
||||||
|
(*st.Commands)[i] = strings.ReplaceAll((*st.Commands)[i], "<USERNAME>", username)
|
||||||
|
}
|
||||||
|
if hostname != "" {
|
||||||
|
(*st.Commands)[i] = strings.ReplaceAll((*st.Commands)[i], "<HOSTNAME>", hostname)
|
||||||
|
}
|
||||||
|
if regRef != "" {
|
||||||
|
(*st.Commands)[i] = strings.ReplaceAll(
|
||||||
|
(*st.Commands)[i],
|
||||||
|
"<REPOSITORY_REFERENCE>", regRef,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if image != nil {
|
||||||
|
(*st.Commands)[i] = strings.ReplaceAll(
|
||||||
|
(*st.Commands)[i],
|
||||||
|
"<IMAGE_NAME>", string(*image),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if tag != nil {
|
||||||
|
(*st.Commands)[i] = strings.ReplaceAll((*st.Commands)[i], "<TAG>", string(*tag))
|
||||||
|
}
|
||||||
|
}
|
179
registry/app/api/controller/metadata/get_registries.go
Normal file
179
registry/app/api/controller/metadata/get_registries.go
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
"github.com/harness/gitness/registry/app/store"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/gotidy/ptr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetAllRegistries(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetAllRegistriesRequestObject,
|
||||||
|
) (artifact.GetAllRegistriesResponseObject, error) {
|
||||||
|
regInfo, _ := c.GetRegistryRequestInfo(
|
||||||
|
ctx, r.Params.PackageType, r.Params.Page, r.Params.Size,
|
||||||
|
r.Params.SearchTerm, RepositoryResource, string(r.SpaceRef), "", nil,
|
||||||
|
r.Params.SortOrder, r.Params.SortField,
|
||||||
|
)
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetAllRegistries400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
if err = apiauth.CheckSpaceScope(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
space,
|
||||||
|
enum.ResourceTypeRegistry,
|
||||||
|
enum.PermissionRegistryView,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetAllRegistries403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var repos *[]store.RegistryMetadata
|
||||||
|
repoType := ""
|
||||||
|
if r.Params.Type != nil {
|
||||||
|
repoType = string(*r.Params.Type)
|
||||||
|
}
|
||||||
|
e := ValidatePackageTypes(regInfo.packageTypes)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
e = ValidateRepoType(repoType)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
var count int64
|
||||||
|
repos, err = c.RegistryRepository.GetAll(
|
||||||
|
ctx,
|
||||||
|
regInfo.parentID,
|
||||||
|
regInfo.packageTypes,
|
||||||
|
regInfo.sortByField,
|
||||||
|
regInfo.sortByOrder,
|
||||||
|
regInfo.limit,
|
||||||
|
regInfo.offset,
|
||||||
|
regInfo.searchTerm,
|
||||||
|
repoType,
|
||||||
|
)
|
||||||
|
count, _ = c.RegistryRepository.CountAll(
|
||||||
|
ctx,
|
||||||
|
regInfo.parentID,
|
||||||
|
regInfo.packageTypes,
|
||||||
|
regInfo.searchTerm,
|
||||||
|
repoType,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetAllRegistries500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return artifact.GetAllRegistries200JSONResponse{
|
||||||
|
ListRegistryResponseJSONResponse: *GetAllRegistryResponse(
|
||||||
|
repos, count, regInfo.pageNumber,
|
||||||
|
regInfo.limit, regInfo.rootIdentifier, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllRegistryResponse(
|
||||||
|
repos *[]store.RegistryMetadata,
|
||||||
|
count int64,
|
||||||
|
pageNumber int64,
|
||||||
|
pageSize int,
|
||||||
|
rootIdentifier string,
|
||||||
|
registryURL string,
|
||||||
|
) *artifact.ListRegistryResponseJSONResponse {
|
||||||
|
repoMetadataList := GetRegistryMetadata(repos, rootIdentifier, registryURL)
|
||||||
|
pageCount := GetPageCount(count, pageSize)
|
||||||
|
listRepository := &artifact.ListRegistry{
|
||||||
|
ItemCount: &count,
|
||||||
|
PageCount: &pageCount,
|
||||||
|
PageIndex: &pageNumber,
|
||||||
|
PageSize: &pageSize,
|
||||||
|
Registries: repoMetadataList,
|
||||||
|
}
|
||||||
|
response := &artifact.ListRegistryResponseJSONResponse{
|
||||||
|
Data: *listRepository,
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRegistryMetadata(
|
||||||
|
registryMetadatas *[]store.RegistryMetadata,
|
||||||
|
rootIdentifier string,
|
||||||
|
registryURL string,
|
||||||
|
) []artifact.RegistryMetadata {
|
||||||
|
repoMetadataList := []artifact.RegistryMetadata{}
|
||||||
|
for _, reg := range *registryMetadatas {
|
||||||
|
modifiedAt := GetTimeInMs(reg.LastModified)
|
||||||
|
var labels *[]string
|
||||||
|
if !commons.IsEmpty(reg.Labels) {
|
||||||
|
temp := []string(reg.Labels)
|
||||||
|
labels = &temp
|
||||||
|
}
|
||||||
|
var description string
|
||||||
|
if !commons.IsEmpty(reg.Description) {
|
||||||
|
description = reg.Description
|
||||||
|
}
|
||||||
|
var artifactCount *int64
|
||||||
|
if reg.ArtifactCount != 0 {
|
||||||
|
artifactCount = ptr.Int64(reg.ArtifactCount)
|
||||||
|
}
|
||||||
|
var downloadCount *int64
|
||||||
|
if reg.DownloadCount != 0 {
|
||||||
|
downloadCount = ptr.Int64(reg.DownloadCount)
|
||||||
|
}
|
||||||
|
// fix: refactor it
|
||||||
|
size := GetSize(reg.Size)
|
||||||
|
repoMetadata := artifact.RegistryMetadata{
|
||||||
|
Identifier: reg.RegIdentifier,
|
||||||
|
Description: &description,
|
||||||
|
PackageType: reg.PackageType,
|
||||||
|
Type: reg.Type,
|
||||||
|
LastModified: &modifiedAt,
|
||||||
|
Url: GetRepoURL(rootIdentifier, reg.RegIdentifier, registryURL),
|
||||||
|
ArtifactsCount: artifactCount,
|
||||||
|
DownloadsCount: downloadCount,
|
||||||
|
RegistrySize: &size,
|
||||||
|
Labels: labels,
|
||||||
|
}
|
||||||
|
repoMetadataList = append(repoMetadataList, repoMetadata)
|
||||||
|
}
|
||||||
|
return repoMetadataList
|
||||||
|
}
|
109
registry/app/api/controller/metadata/get_registry.go
Normal file
109
registry/app/api/controller/metadata/get_registry.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) GetRegistry(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.GetRegistryRequestObject,
|
||||||
|
) (artifact.GetRegistryResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.GetRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.GetRegistry403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
repoEntity, _ := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if string(repoEntity.Type) == string(artifact.RegistryTypeVIRTUAL) {
|
||||||
|
cleanupPolicies, err := c.CleanupPolicyStore.GetByRegistryID(ctx, repoEntity.ID)
|
||||||
|
if err != nil {
|
||||||
|
return throwGetRegistry500Error(err), nil
|
||||||
|
}
|
||||||
|
if len(repoEntity.Name) == 0 {
|
||||||
|
return artifact.GetRegistry404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "registry doesn't exist with this key"),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return artifact.GetRegistry200JSONResponse{
|
||||||
|
RegistryResponseJSONResponse: *CreateVirtualRepositoryResponse(
|
||||||
|
repoEntity, c.getUpstreamProxyKeys(
|
||||||
|
ctx,
|
||||||
|
repoEntity.UpstreamProxies,
|
||||||
|
), cleanupPolicies, regInfo.rootIdentifier, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
upstreamproxyEntity, err := c.UpstreamProxyStore.GetByRegistryIdentifier(
|
||||||
|
ctx,
|
||||||
|
regInfo.parentID, regInfo.RegistryIdentifier,
|
||||||
|
)
|
||||||
|
if len(upstreamproxyEntity.RepoKey) == 0 {
|
||||||
|
return artifact.GetRegistry404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "registry doesn't exist with this key"),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return throwGetRegistry500Error(err), nil
|
||||||
|
}
|
||||||
|
return artifact.GetRegistry200JSONResponse{
|
||||||
|
RegistryResponseJSONResponse: *CreateUpstreamProxyResponseJSONResponse(upstreamproxyEntity),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func throwGetRegistry500Error(err error) artifact.GetRegistry500JSONResponse {
|
||||||
|
return artifact.GetRegistry500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
138
registry/app/api/controller/metadata/update_artifact.go
Normal file
138
registry/app/api/controller/metadata/update_artifact.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) UpdateArtifactLabels(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.UpdateArtifactLabelsRequestObject,
|
||||||
|
) (artifact.UpdateArtifactLabelsResponseObject, error) {
|
||||||
|
regInfo, _ := c.GetRegistryRequestInfo(
|
||||||
|
ctx, nil, nil, nil, nil,
|
||||||
|
ArtifactVersionResource, "", string(r.RegistryRef), nil, nil, nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.UpdateArtifactLabels400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryEdit)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.UpdateArtifactLabels403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
a := string(r.Artifact)
|
||||||
|
|
||||||
|
artifactEntity, err := c.ArtifactStore.GetByRepoAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier, a)
|
||||||
|
|
||||||
|
if len(artifactEntity.Name) == 0 {
|
||||||
|
return artifact.UpdateArtifactLabels404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "artifact doesn't exist with this name"),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyArtifact400Error(err), nil
|
||||||
|
}
|
||||||
|
existingArtifact, err := AttachLabels(artifact.ArtifactLabelRequest(*r.Body), artifactEntity)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyArtifact400Error(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.ArtifactStore.Update(ctx, existingArtifact)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyArtifact400Error(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := c.TagStore.GetLatestTagMetadata(ctx, regInfo.parentID, regInfo.RegistryIdentifier, a)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return artifact.UpdateArtifactLabels500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return artifact.UpdateArtifactLabels200JSONResponse{
|
||||||
|
ArtifactLabelResponseJSONResponse: *getArtifactSummary(*tag),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func throwModifyArtifact400Error(err error) artifact.UpdateArtifactLabels400JSONResponse {
|
||||||
|
return artifact.UpdateArtifactLabels400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AttachLabels(
|
||||||
|
dto artifact.ArtifactLabelRequest,
|
||||||
|
existingArtifact *types.Artifact,
|
||||||
|
) (*types.Artifact, error) {
|
||||||
|
return &types.Artifact{
|
||||||
|
ID: existingArtifact.ID,
|
||||||
|
RegistryID: existingArtifact.RegistryID,
|
||||||
|
Name: existingArtifact.Name,
|
||||||
|
Labels: dto.Labels,
|
||||||
|
CreatedAt: existingArtifact.CreatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArtifactSummary(t types.ArtifactMetadata) *artifact.ArtifactLabelResponseJSONResponse {
|
||||||
|
downloads := int64(0)
|
||||||
|
createdAt := GetTimeInMs(t.CreatedAt)
|
||||||
|
modifiedAt := GetTimeInMs(t.ModifiedAt)
|
||||||
|
artifactVersionSummary := &artifact.ArtifactSummary{
|
||||||
|
CreatedAt: &createdAt,
|
||||||
|
ModifiedAt: &modifiedAt,
|
||||||
|
DownloadsCount: &downloads,
|
||||||
|
ImageName: t.Name,
|
||||||
|
Labels: &t.Labels,
|
||||||
|
PackageType: t.PackageType,
|
||||||
|
}
|
||||||
|
response := &artifact.ArtifactLabelResponseJSONResponse{
|
||||||
|
Data: *artifactVersionSummary,
|
||||||
|
Status: artifact.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
398
registry/app/api/controller/metadata/update_registry.go
Normal file
398
registry/app/api/controller/metadata/update_registry.go
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/audit"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/types"
|
||||||
|
types2 "github.com/harness/gitness/types"
|
||||||
|
gitnessenum "github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *APIController) ModifyRegistry(
|
||||||
|
ctx context.Context,
|
||||||
|
r artifact.ModifyRegistryRequestObject,
|
||||||
|
) (artifact.ModifyRegistryResponseObject, error) {
|
||||||
|
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||||
|
if err != nil {
|
||||||
|
return artifact.ModifyRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, regInfo.parentRef)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.ModifyRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
permissionChecks := getPermissionChecks(space, regInfo.RegistryIdentifier, gitnessenum.PermissionRegistryEdit)
|
||||||
|
if err = apiauth.CheckRegistry(
|
||||||
|
ctx,
|
||||||
|
c.Authorizer,
|
||||||
|
session,
|
||||||
|
permissionChecks...,
|
||||||
|
); err != nil {
|
||||||
|
return artifact.ModifyRegistry403JSONResponse{
|
||||||
|
UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||||
|
),
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoEntity, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(repoEntity.Type) == string(artifact.RegistryTypeVIRTUAL) {
|
||||||
|
return c.updateVirtualRegistry(ctx, r, repoEntity, err, regInfo, session)
|
||||||
|
}
|
||||||
|
upstreamproxyEntity, err := c.UpstreamProxyStore.GetByRegistryIdentifier(
|
||||||
|
ctx, regInfo.parentID,
|
||||||
|
regInfo.RegistryIdentifier,
|
||||||
|
)
|
||||||
|
if len(upstreamproxyEntity.RepoKey) == 0 {
|
||||||
|
return artifact.ModifyRegistry404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "registry doesn't exist with this key"),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
registry, upstreamproxy, err := UpdateUpstreamProxyEntity(
|
||||||
|
artifact.RegistryRequest(*r.Body),
|
||||||
|
regInfo.parentID, regInfo.rootIdentifierID, upstreamproxyEntity,
|
||||||
|
)
|
||||||
|
registry.ID = repoEntity.ID
|
||||||
|
upstreamproxy.ID = upstreamproxyEntity.ID
|
||||||
|
upstreamproxy.RegistryID = repoEntity.ID
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
err = c.tx.WithTx(
|
||||||
|
ctx, func(ctx context.Context) error {
|
||||||
|
err = c.updateRegistryWithAudit(ctx, repoEntity, registry, session.Principal, regInfo.parentRef)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update registry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.updateUpstreamProxyWithAudit(
|
||||||
|
ctx, upstreamproxy, session.Principal, regInfo.parentRef, registry.Name,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update upstream proxy: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
modifiedRepoEntity, err := c.UpstreamProxyStore.Get(ctx, upstreamproxyEntity.RegistryID)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
return artifact.ModifyRegistry200JSONResponse{
|
||||||
|
RegistryResponseJSONResponse: *CreateUpstreamProxyResponseJSONResponse(modifiedRepoEntity),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) updateVirtualRegistry(
|
||||||
|
ctx context.Context, r artifact.ModifyRegistryRequestObject, repoEntity *types.Registry, err error,
|
||||||
|
regInfo *RegistryRequestBaseInfo, session *auth.Session,
|
||||||
|
) (artifact.ModifyRegistryResponseObject, error) {
|
||||||
|
if len(repoEntity.Name) == 0 {
|
||||||
|
return artifact.ModifyRegistry404JSONResponse{
|
||||||
|
NotFoundJSONResponse: artifact.NotFoundJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusNotFound, "registry doesn't exist with this key"),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), err
|
||||||
|
}
|
||||||
|
registry, err := UpdateRepoEntity(
|
||||||
|
artifact.RegistryRequest(*r.Body),
|
||||||
|
repoEntity.ParentID,
|
||||||
|
repoEntity.RootParentID,
|
||||||
|
repoEntity,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return artifact.ModifyRegistry400JSONResponse{
|
||||||
|
BadRequestJSONResponse: artifact.BadRequestJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
err = c.setUpstreamProxyIDs(ctx, registry, artifact.RegistryRequest(*r.Body), regInfo.parentID)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), nil
|
||||||
|
}
|
||||||
|
err = c.updateRegistryWithAudit(ctx, repoEntity, registry, session.Principal, regInfo.parentRef)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), nil
|
||||||
|
}
|
||||||
|
err = c.updateCleanupPolicy(ctx, r.Body, registry.ID)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), nil
|
||||||
|
}
|
||||||
|
modifiedRepoEntity, err := c.RegistryRepository.Get(ctx, registry.ID)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), nil
|
||||||
|
}
|
||||||
|
cleanupPolicies, err := c.CleanupPolicyStore.GetByRegistryID(ctx, repoEntity.ID)
|
||||||
|
if err != nil {
|
||||||
|
return throwModifyRegistry500Error(err), nil
|
||||||
|
}
|
||||||
|
return artifact.ModifyRegistry200JSONResponse{
|
||||||
|
RegistryResponseJSONResponse: *CreateVirtualRepositoryResponse(
|
||||||
|
modifiedRepoEntity,
|
||||||
|
c.getUpstreamProxyKeys(ctx, modifiedRepoEntity.UpstreamProxies), cleanupPolicies,
|
||||||
|
regInfo.rootIdentifier, c.URLProvider.RegistryURL(),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) updateUpstreamProxyWithAudit(
|
||||||
|
ctx context.Context, upstreamProxy *types.UpstreamProxyConfig,
|
||||||
|
principal types2.Principal, parentRef string, registryName string,
|
||||||
|
) error {
|
||||||
|
existingUpstreamProxy, err := c.UpstreamProxyStore.Get(ctx, upstreamProxy.RegistryID)
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf(
|
||||||
|
"failed to fig upstream proxy config for: %d",
|
||||||
|
upstreamProxy.RegistryID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.UpstreamProxyStore.Update(ctx, upstreamProxy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if existingUpstreamProxy != nil {
|
||||||
|
auditErr := c.AuditService.Log(
|
||||||
|
ctx,
|
||||||
|
principal,
|
||||||
|
audit.NewResource(audit.ResourceTypeRegistryUpstreamProxy, registryName),
|
||||||
|
audit.ActionUpdated,
|
||||||
|
parentRef,
|
||||||
|
audit.WithOldObject(
|
||||||
|
audit.RegistryUpstreamProxyConfigObject{
|
||||||
|
ID: existingUpstreamProxy.ID,
|
||||||
|
RegistryID: existingUpstreamProxy.RegistryID,
|
||||||
|
Source: existingUpstreamProxy.Source,
|
||||||
|
URL: existingUpstreamProxy.RepoURL,
|
||||||
|
AuthType: existingUpstreamProxy.RepoAuthType,
|
||||||
|
CreatedAt: existingUpstreamProxy.CreatedAt,
|
||||||
|
UpdatedAt: existingUpstreamProxy.UpdatedAt,
|
||||||
|
CreatedBy: existingUpstreamProxy.CreatedBy,
|
||||||
|
UpdatedBy: existingUpstreamProxy.UpdatedBy,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
audit.WithNewObject(
|
||||||
|
audit.RegistryUpstreamProxyConfigObject{
|
||||||
|
ID: upstreamProxy.ID,
|
||||||
|
RegistryID: upstreamProxy.RegistryID,
|
||||||
|
Source: upstreamProxy.Source,
|
||||||
|
URL: upstreamProxy.URL,
|
||||||
|
AuthType: upstreamProxy.AuthType,
|
||||||
|
CreatedAt: upstreamProxy.CreatedAt,
|
||||||
|
UpdatedAt: upstreamProxy.UpdatedAt,
|
||||||
|
CreatedBy: upstreamProxy.CreatedBy,
|
||||||
|
UpdatedBy: upstreamProxy.UpdatedBy,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if auditErr != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf(
|
||||||
|
"failed to insert audit log for update upstream proxy "+
|
||||||
|
"config operation: %s", auditErr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) updateRegistryWithAudit(
|
||||||
|
ctx context.Context, oldRegistry *types.Registry,
|
||||||
|
newRegistry *types.Registry, principal types2.Principal, parentRef string,
|
||||||
|
) error {
|
||||||
|
err := c.RegistryRepository.Update(ctx, newRegistry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
auditErr := c.AuditService.Log(
|
||||||
|
ctx,
|
||||||
|
principal,
|
||||||
|
audit.NewResource(audit.ResourceTypeRegistry, newRegistry.Name),
|
||||||
|
audit.ActionUpdated,
|
||||||
|
parentRef,
|
||||||
|
audit.WithOldObject(newRegistry),
|
||||||
|
audit.WithNewObject(oldRegistry),
|
||||||
|
)
|
||||||
|
if auditErr != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("failed to insert audit log for update registry operation: %s", auditErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func throwModifyRegistry500Error(err error) artifact.ModifyRegistry500JSONResponse {
|
||||||
|
return artifact.ModifyRegistry500JSONResponse{
|
||||||
|
InternalServerErrorJSONResponse: artifact.InternalServerErrorJSONResponse(
|
||||||
|
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIController) updateCleanupPolicy(
|
||||||
|
ctx context.Context, config *artifact.ModifyRegistryJSONRequestBody, registryID int64,
|
||||||
|
) error {
|
||||||
|
existingCleanupPolicies, err := c.CleanupPolicyStore.GetIDsByRegistryID(ctx, registryID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
currentCleanupPolicyEntities := CreateCleanupPolicyEntity(config, registryID)
|
||||||
|
|
||||||
|
err = c.CleanupPolicyStore.ModifyCleanupPolicies(ctx, currentCleanupPolicyEntities, existingCleanupPolicies)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateRepoEntity(
|
||||||
|
dto artifact.RegistryRequest,
|
||||||
|
parentID int64,
|
||||||
|
rootParentID int64,
|
||||||
|
existingRepo *types.Registry,
|
||||||
|
) (*types.Registry, error) {
|
||||||
|
allowedPattern, blockedPattern, description, labels := getRepoEntityFields(dto)
|
||||||
|
e := ValidatePackageTypeChange(string(existingRepo.PackageType), string(dto.PackageType))
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
e = ValidateRepoTypeChange(string(existingRepo.Type), string(dto.Config.Type))
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
e = ValidateIdentifierChange(existingRepo.Name, dto.Identifier)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
entity := &types.Registry{
|
||||||
|
Name: dto.Identifier,
|
||||||
|
ID: existingRepo.ID,
|
||||||
|
ParentID: parentID,
|
||||||
|
RootParentID: rootParentID,
|
||||||
|
Description: description,
|
||||||
|
AllowedPattern: allowedPattern,
|
||||||
|
BlockedPattern: blockedPattern,
|
||||||
|
PackageType: existingRepo.PackageType,
|
||||||
|
Type: existingRepo.Type,
|
||||||
|
Labels: labels,
|
||||||
|
CreatedAt: existingRepo.CreatedAt,
|
||||||
|
}
|
||||||
|
return entity, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateUpstreamProxyEntity(
|
||||||
|
dto artifact.RegistryRequest,
|
||||||
|
parentID int64,
|
||||||
|
rootParentID int64,
|
||||||
|
u *types.UpstreamProxy,
|
||||||
|
) (*types.Registry, *types.UpstreamProxyConfig, error) {
|
||||||
|
allowedPattern := []string{}
|
||||||
|
if dto.AllowedPattern != nil {
|
||||||
|
allowedPattern = *dto.AllowedPattern
|
||||||
|
}
|
||||||
|
blockedPattern := []string{}
|
||||||
|
if dto.BlockedPattern != nil {
|
||||||
|
blockedPattern = *dto.BlockedPattern
|
||||||
|
}
|
||||||
|
e := ValidatePackageTypeChange(string(u.PackageType), string(dto.PackageType))
|
||||||
|
if e != nil {
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
e = ValidateIdentifierChange(u.RepoKey, dto.Identifier)
|
||||||
|
if e != nil {
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
repoEntity := &types.Registry{
|
||||||
|
ID: u.RegistryID,
|
||||||
|
Name: dto.Identifier,
|
||||||
|
ParentID: parentID,
|
||||||
|
RootParentID: rootParentID,
|
||||||
|
AllowedPattern: allowedPattern,
|
||||||
|
BlockedPattern: blockedPattern,
|
||||||
|
PackageType: dto.PackageType,
|
||||||
|
Type: artifact.RegistryTypeUPSTREAM,
|
||||||
|
CreatedAt: u.CreatedAt,
|
||||||
|
}
|
||||||
|
config, _ := dto.Config.AsUpstreamConfig()
|
||||||
|
CleanURLPath(config.Url)
|
||||||
|
upstreamProxyConfigEntity := &types.UpstreamProxyConfig{
|
||||||
|
URL: *config.Url,
|
||||||
|
AuthType: string(config.AuthType),
|
||||||
|
RegistryID: u.RegistryID,
|
||||||
|
CreatedAt: u.CreatedAt,
|
||||||
|
}
|
||||||
|
if config.Source != nil && len(string(*config.Source)) > 0 {
|
||||||
|
err := ValidateUpstreamSource(string(*config.Source))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
upstreamProxyConfigEntity.Source = string(*config.Source)
|
||||||
|
}
|
||||||
|
if string(artifact.UpstreamConfigSourceDockerhub) == string(*config.Source) {
|
||||||
|
upstreamProxyConfigEntity.URL = ""
|
||||||
|
}
|
||||||
|
if u.ID != -1 {
|
||||||
|
upstreamProxyConfigEntity.ID = u.ID
|
||||||
|
}
|
||||||
|
if config.AuthType == artifact.AuthTypeUserPassword {
|
||||||
|
res, err := config.Auth.AsUserPassword()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
upstreamProxyConfigEntity.UserName = res.UserName
|
||||||
|
upstreamProxyConfigEntity.SecretIdentifier = *res.SecretIdentifier
|
||||||
|
upstreamProxyConfigEntity.SecretSpaceID = *res.SecretSpaceId
|
||||||
|
} else {
|
||||||
|
upstreamProxyConfigEntity.UserName = ""
|
||||||
|
upstreamProxyConfigEntity.SecretIdentifier = ""
|
||||||
|
upstreamProxyConfigEntity.SecretSpaceID = 0
|
||||||
|
}
|
||||||
|
return repoEntity, upstreamProxyConfigEntity, nil
|
||||||
|
}
|
408
registry/app/api/controller/metadata/utils.go
Normal file
408
registry/app/api/controller/metadata/utils.go
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registrySort = []string{
|
||||||
|
"identifier",
|
||||||
|
"lastModified",
|
||||||
|
"registrySize",
|
||||||
|
"artifactsCount",
|
||||||
|
"downloadsCount",
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RepositoryResource = "repository"
|
||||||
|
ArtifactResource = "artifact"
|
||||||
|
ArtifactVersionResource = "artifactversion"
|
||||||
|
RegistryIdentifierErrorMsg = "registry name should be 1~255 characters long with lower case characters, numbers " +
|
||||||
|
"and ._- and must be start with numbers or characters"
|
||||||
|
RegexIdentifierPattern = "^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RegistrySortMap = map[string]string{
|
||||||
|
"identifier": "name",
|
||||||
|
"lastModified": "updated_at",
|
||||||
|
"registrySize": "size",
|
||||||
|
"artifactsCount": "artifact_count",
|
||||||
|
"downloadsCount": "download_count",
|
||||||
|
"createdAt": "created_at",
|
||||||
|
}
|
||||||
|
|
||||||
|
var artifactSort = []string{
|
||||||
|
"repoKey",
|
||||||
|
"name",
|
||||||
|
"lastModified",
|
||||||
|
"downloadsCount",
|
||||||
|
}
|
||||||
|
|
||||||
|
var artifactSortMap = map[string]string{
|
||||||
|
"repoKey": "name",
|
||||||
|
"lastModified": "updated_at",
|
||||||
|
"name": "image_name",
|
||||||
|
"downloadsCount": "image_name",
|
||||||
|
"createdAt": "created_at",
|
||||||
|
}
|
||||||
|
|
||||||
|
var artifactVersionSort = []string{
|
||||||
|
"name",
|
||||||
|
"size",
|
||||||
|
"pullCommand",
|
||||||
|
"downloadsCount",
|
||||||
|
"lastModified",
|
||||||
|
}
|
||||||
|
|
||||||
|
var artifactVersionSortMap = map[string]string{
|
||||||
|
"name": "name",
|
||||||
|
"size": "name",
|
||||||
|
"pullCommand": "name",
|
||||||
|
"downloadsCount": "name",
|
||||||
|
"lastModified": "updated_at",
|
||||||
|
"createdAt": "created_at",
|
||||||
|
}
|
||||||
|
|
||||||
|
var validRepositoryTypes = []string{
|
||||||
|
string(api.RegistryTypeUPSTREAM),
|
||||||
|
string(api.RegistryTypeVIRTUAL),
|
||||||
|
}
|
||||||
|
|
||||||
|
var validPackageTypes = []string{
|
||||||
|
string(api.PackageTypeDOCKER),
|
||||||
|
string(api.PackageTypeHELM),
|
||||||
|
string(api.PackageTypeMAVEN),
|
||||||
|
}
|
||||||
|
|
||||||
|
var validUpstreamSources = []string{
|
||||||
|
string(api.UpstreamConfigSourceCustom),
|
||||||
|
string(api.UpstreamConfigSourceDockerhub),
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidatePackageTypes(packageTypes []string) error {
|
||||||
|
if commons.IsEmpty(packageTypes) || IsPackageTypesValid(packageTypes) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("invalid package type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidatePackageType(packageType string) error {
|
||||||
|
if len(packageType) == 0 || IsPackageTypeValid(packageType) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("invalid package type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidatePackageTypeChange(fromDB, newPackage string) error {
|
||||||
|
if len(fromDB) > 0 && len(newPackage) > 0 && fromDB == newPackage {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("package type change is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateRepoTypeChange(fromDB, newRepo string) error {
|
||||||
|
if len(fromDB) > 0 && len(newRepo) > 0 && fromDB == newRepo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("registry type change is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateIdentifierChange(fromDB, newIdentifier string) error {
|
||||||
|
if len(fromDB) > 0 && len(newIdentifier) > 0 && fromDB == newIdentifier {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("registry identifier change is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateIdentifier(identifier string) error {
|
||||||
|
if len(identifier) == 0 {
|
||||||
|
return errors.New(RegistryIdentifierErrorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
matched, err := regexp.MatchString(RegexIdentifierPattern, identifier)
|
||||||
|
if err != nil || !matched {
|
||||||
|
return errors.New(RegistryIdentifierErrorMsg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateUpstream(config *api.RegistryConfig) error {
|
||||||
|
if !commons.IsEmpty(config.Type) && config.Type == api.RegistryTypeUPSTREAM {
|
||||||
|
upstreamConfig, err := config.AsUpstreamConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if commons.IsEmpty(upstreamConfig.Url) {
|
||||||
|
return errors.New("URL is required for upstream repository")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateRepoType(repoType string) error {
|
||||||
|
if len(repoType) == 0 || IsRepoTypeValid(repoType) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("invalid repository type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateUpstreamSource(source string) error {
|
||||||
|
if len(source) == 0 || IsUpstreamSourceValid(source) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("invalid upstream proxy source")
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRepoTypeValid(repoType string) bool {
|
||||||
|
for _, item := range validRepositoryTypes {
|
||||||
|
if item == repoType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsUpstreamSourceValid(source string) bool {
|
||||||
|
for _, item := range validUpstreamSources {
|
||||||
|
if item == source {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPackageTypeValid(packageType string) bool {
|
||||||
|
for _, item := range validPackageTypes {
|
||||||
|
if item == packageType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPackageTypesValid(packageTypes []string) bool {
|
||||||
|
for _, item := range packageTypes {
|
||||||
|
if !IsPackageTypeValid(item) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTimeInMs(t time.Time) string {
|
||||||
|
return fmt.Sprint(t.UnixMilli())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetErrorResponse(code int, message string) *api.Error {
|
||||||
|
return &api.Error{
|
||||||
|
Code: fmt.Sprint(code),
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSortByOrder(sortOrder string) string {
|
||||||
|
defaultSortOrder := "ASC"
|
||||||
|
decreasingSortOrder := "DESC"
|
||||||
|
if len(sortOrder) == 0 {
|
||||||
|
return defaultSortOrder
|
||||||
|
}
|
||||||
|
if sortOrder == decreasingSortOrder {
|
||||||
|
return decreasingSortOrder
|
||||||
|
}
|
||||||
|
return defaultSortOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortKey(slice []string, target string) string {
|
||||||
|
for _, item := range slice {
|
||||||
|
if item == target {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "createdAt"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSortByField(sortByField string, resource string) string {
|
||||||
|
switch resource {
|
||||||
|
case RepositoryResource:
|
||||||
|
sortkey := sortKey(registrySort, sortByField)
|
||||||
|
return RegistrySortMap[sortkey]
|
||||||
|
case ArtifactResource:
|
||||||
|
sortkey := sortKey(artifactSort, sortByField)
|
||||||
|
return artifactSortMap[sortkey]
|
||||||
|
case ArtifactVersionResource:
|
||||||
|
sortkey := sortKey(artifactVersionSort, sortByField)
|
||||||
|
return artifactVersionSortMap[sortkey]
|
||||||
|
}
|
||||||
|
return "created_at"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPageLimit(pageSize *api.PageSize) int {
|
||||||
|
defaultPageSize := 10
|
||||||
|
if pageSize != nil {
|
||||||
|
return int(*pageSize)
|
||||||
|
}
|
||||||
|
return defaultPageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOffset(pageSize *api.PageSize, pageNumber *api.PageNumber) int {
|
||||||
|
defaultOffset := 0
|
||||||
|
if pageSize == nil || pageNumber == nil {
|
||||||
|
return defaultOffset
|
||||||
|
}
|
||||||
|
if *pageNumber == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (int(*pageSize)) * int(*pageNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPageNumber(pageNumber *api.PageNumber) int64 {
|
||||||
|
defaultPageNumber := int64(1)
|
||||||
|
if pageNumber == nil {
|
||||||
|
return defaultPageNumber
|
||||||
|
}
|
||||||
|
return int64(*pageNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSuccessResponse() *api.Success {
|
||||||
|
return &api.Success{
|
||||||
|
Status: api.StatusSUCCESS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPageCount(count int64, pageSize int) int64 {
|
||||||
|
return int64(math.Ceil(float64(count) / float64(pageSize)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetImageSize(size string) string {
|
||||||
|
sizeVal, _ := strconv.ParseInt(size, 10, 64)
|
||||||
|
return GetSize(sizeVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSize(sizeVal int64) string {
|
||||||
|
humanReadable := humanize.Bytes(uint64(sizeVal))
|
||||||
|
return humanReadable
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRegRef(parentRef string, regIdentifier string) (string, error) {
|
||||||
|
result := ""
|
||||||
|
if commons.IsEmpty(parentRef) || commons.IsEmpty(regIdentifier) {
|
||||||
|
return result, errors.New("parentRef or regIdentifier is empty")
|
||||||
|
}
|
||||||
|
return parentRef + "/" + regIdentifier, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRepoURL(rootIdentifier, registry string, registryURL string) string {
|
||||||
|
parsedURL, err := url.Parse(registryURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Error parsing URL: %s", registryURL)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
parsedURL.Path = path.Join(parsedURL.Path, strings.ToLower(rootIdentifier), registry)
|
||||||
|
return parsedURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRepoURLWithoutProtocol(rootIdentifier string, registry string, registryURL string) string {
|
||||||
|
repoURL := GetRepoURL(rootIdentifier, registry, registryURL)
|
||||||
|
parsedURL, err := url.Parse(repoURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Stack().Err(err).Msg("Error parsing URL: ")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedURL.Host + parsedURL.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTagURL(rootIdentifier string, artifact string, version string, registry string, registryURL string) string {
|
||||||
|
url := GetRepoURL(rootIdentifier, registry, registryURL)
|
||||||
|
url += "/" + artifact + "/"
|
||||||
|
url += version
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPullCommand(
|
||||||
|
rootIdentifier string, registry string, image string, tag string,
|
||||||
|
packageType string, registryURL string,
|
||||||
|
) string {
|
||||||
|
if packageType == "DOCKER" {
|
||||||
|
return GetDockerPullCommand(rootIdentifier, registry, image, tag, registryURL)
|
||||||
|
} else if packageType == "HELM" {
|
||||||
|
return GetHelmPullCommand(rootIdentifier, registry, image, tag, registryURL)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDockerPullCommand(
|
||||||
|
rootIdentifier string, registry string, image string,
|
||||||
|
tag string, registryURL string,
|
||||||
|
) string {
|
||||||
|
return "docker pull " + GetRepoURLWithoutProtocol(rootIdentifier, registry, registryURL) + "/" + image + ":" + tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHelmPullCommand(rootIdentifier string, registry string, image string, tag string, registryURL string) string {
|
||||||
|
return "helm install " + GetRepoURLWithoutProtocol(rootIdentifier, registry, registryURL) + "/" + image + ":" + tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanURLPath removes leading and trailing spaces and trailing slashes from the given URL string.
|
||||||
|
func CleanURLPath(input *string) {
|
||||||
|
if input == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Parse the input to URL
|
||||||
|
u, err := url.Parse(*input)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean the path by removing trailing slashes and spaces
|
||||||
|
cleanedPath := strings.TrimRight(strings.TrimSpace(u.Path), "/")
|
||||||
|
|
||||||
|
// Update the URL path in the original input string
|
||||||
|
u.Path = cleanedPath
|
||||||
|
|
||||||
|
// Update the input string with the cleaned URL string representation
|
||||||
|
*input = u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPermissionChecks(
|
||||||
|
space *types.Space,
|
||||||
|
registryIdentifier string,
|
||||||
|
permission enum.Permission,
|
||||||
|
) []types.PermissionCheck {
|
||||||
|
var permissionChecks []types.PermissionCheck
|
||||||
|
permissionCheck := &types.PermissionCheck{
|
||||||
|
Scope: types.Scope{SpacePath: space.Identifier},
|
||||||
|
Resource: types.Resource{Type: enum.ResourceTypeRegistry, Identifier: registryIdentifier},
|
||||||
|
Permission: permission,
|
||||||
|
}
|
||||||
|
permissionChecks = append(permissionChecks, *permissionCheck)
|
||||||
|
return permissionChecks
|
||||||
|
}
|
90
registry/app/api/handler/oci/artifactfilter.go
Normal file
90
registry/app/api/handler/oci/artifactfilter.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MatchArtifactFilter(
|
||||||
|
allowedPattern pq.StringArray,
|
||||||
|
blockedPattern pq.StringArray, artifact string,
|
||||||
|
) (bool, error) {
|
||||||
|
allowedPatterns := []string(allowedPattern)
|
||||||
|
blockedPatterns := []string(blockedPattern)
|
||||||
|
|
||||||
|
if len(blockedPatterns) > 0 {
|
||||||
|
flag, err := matchPatterns(blockedPatterns, artifact)
|
||||||
|
if err != nil {
|
||||||
|
return flag, fmt.Errorf(
|
||||||
|
"failed to match blocked patterns for artifact %s: %w",
|
||||||
|
artifact, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if flag {
|
||||||
|
return false, errors.New(
|
||||||
|
"failed because artifact seems to be matching blocked patterns configured on repository",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allowedPatterns) > 0 {
|
||||||
|
flag, err := matchPatterns(allowedPatterns, artifact)
|
||||||
|
if err != nil {
|
||||||
|
return flag, fmt.Errorf(
|
||||||
|
"failed to match allowed patterns for artifact %s: %w",
|
||||||
|
artifact, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !flag {
|
||||||
|
return false, errors.New(
|
||||||
|
"failed because artifact doesn't seems to be matching allowed patterns configured on repository",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchPatterns(
|
||||||
|
patterns []string,
|
||||||
|
val string,
|
||||||
|
) (bool, error) {
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
flag, err := regexp.MatchString(pattern, val)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf(
|
||||||
|
"failed to match pattern %s for val %s",
|
||||||
|
pattern,
|
||||||
|
val,
|
||||||
|
)
|
||||||
|
return flag, fmt.Errorf(
|
||||||
|
"failed to match pattern %s for val %s: %w",
|
||||||
|
pattern,
|
||||||
|
val,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if flag {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
249
registry/app/api/handler/oci/base.go
Normal file
249
registry/app/api/handler/oci/base.go
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
usercontroller "github.com/harness/gitness/app/api/controller/user"
|
||||||
|
"github.com/harness/gitness/app/auth/authn"
|
||||||
|
"github.com/harness/gitness/app/auth/authz"
|
||||||
|
corestore "github.com/harness/gitness/app/store"
|
||||||
|
urlprovider "github.com/harness/gitness/app/url"
|
||||||
|
"github.com/harness/gitness/registry/app/api/controller/metadata"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
"github.com/harness/gitness/registry/app/dist_temp/dcontext"
|
||||||
|
"github.com/harness/gitness/registry/app/dist_temp/errcode"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
|
||||||
|
v2 "github.com/distribution/distribution/v3/registry/api/v2"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHandler(
|
||||||
|
controller *docker.Controller, spaceStore corestore.SpaceStore, tokenStore corestore.TokenStore,
|
||||||
|
userCtrl *usercontroller.Controller, authenticator authn.Authenticator, urlProvider urlprovider.Provider,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
) *Handler {
|
||||||
|
return &Handler{
|
||||||
|
Controller: controller,
|
||||||
|
SpaceStore: spaceStore,
|
||||||
|
TokenStore: tokenStore,
|
||||||
|
UserCtrl: userCtrl,
|
||||||
|
Authenticator: authenticator,
|
||||||
|
URLProvider: urlProvider,
|
||||||
|
Authorizer: authorizer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
Controller *docker.Controller
|
||||||
|
SpaceStore corestore.SpaceStore
|
||||||
|
TokenStore corestore.TokenStore
|
||||||
|
UserCtrl *usercontroller.Controller
|
||||||
|
Authenticator authn.Authenticator
|
||||||
|
URLProvider urlprovider.Provider
|
||||||
|
Authorizer authz.Authorizer
|
||||||
|
}
|
||||||
|
|
||||||
|
type routeType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Manifests routeType = "manifests" // /v2/:registry/:image/manifests/:reference.
|
||||||
|
Blobs routeType = "blobs" // /v2/:registry/:image/blobs/:digest.
|
||||||
|
BlobsUploadsSession routeType = "blob-uploads-session" // /v2/:registry/:image/blobs/uploads/:session_id.
|
||||||
|
Tags routeType = "tags" // /v2/:registry/:image/tags/list.
|
||||||
|
Referrers routeType = "referrers" // /v2/:registry/:image/referrers/:digest.
|
||||||
|
Invalid routeType = "invalid" // Invalid route.
|
||||||
|
MinSizeOfURLSegments = 5
|
||||||
|
|
||||||
|
APIPartManifest = "manifests"
|
||||||
|
APIPartBlobs = "blobs"
|
||||||
|
APIPartUpload = "uploads"
|
||||||
|
APIPartTag = "tags"
|
||||||
|
APIPartReferrer = "referrers"
|
||||||
|
// Add other route types here.
|
||||||
|
)
|
||||||
|
|
||||||
|
func getRouteType(url string) routeType {
|
||||||
|
url = strings.Trim(url, "/")
|
||||||
|
segments := strings.Split(url, "/")
|
||||||
|
if len(segments) < MinSizeOfURLSegments {
|
||||||
|
return Invalid
|
||||||
|
}
|
||||||
|
typ := segments[len(segments)-2]
|
||||||
|
switch typ {
|
||||||
|
case APIPartManifest:
|
||||||
|
return Manifests
|
||||||
|
case APIPartBlobs:
|
||||||
|
if segments[len(segments)-1] == APIPartUpload {
|
||||||
|
return BlobsUploadsSession
|
||||||
|
}
|
||||||
|
return Blobs
|
||||||
|
case APIPartUpload:
|
||||||
|
return BlobsUploadsSession
|
||||||
|
case APIPartTag:
|
||||||
|
return Tags
|
||||||
|
case APIPartReferrer:
|
||||||
|
return Referrers
|
||||||
|
}
|
||||||
|
return Invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetQueryParamMap(queryParams url.Values) map[string]string {
|
||||||
|
queryMap := make(map[string]string)
|
||||||
|
for key, values := range queryParams {
|
||||||
|
if len(values) > 0 {
|
||||||
|
queryMap[key] = values[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return queryMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractPathVars extracts registry, image, reference, digest and tag from the path
|
||||||
|
// Path format: /v2/:rootSpace/:registry/:image/manifests/:reference (for ex:
|
||||||
|
// /v2/myRootSpace/reg1/alpine/blobs/sha256:a258b2a6b59a7aa244d8ceab095c7f8df726f27075a69fca7ad8490f3f63148a).
|
||||||
|
func ExtractPathVars(path string, paramMap map[string]string) (rootIdentifier, registry, image, ref, dgst, tag string) {
|
||||||
|
path = strings.Trim(path, "/")
|
||||||
|
segments := strings.Split(path, "/")
|
||||||
|
rootIdentifier = segments[1]
|
||||||
|
registry = segments[2]
|
||||||
|
image = strings.Join(segments[3:len(segments)-2], "/")
|
||||||
|
typ := getRouteType(path)
|
||||||
|
|
||||||
|
switch typ {
|
||||||
|
case Manifests:
|
||||||
|
ref = segments[len(segments)-1]
|
||||||
|
_, err := digest.Parse(ref)
|
||||||
|
if err != nil {
|
||||||
|
tag = ref
|
||||||
|
} else {
|
||||||
|
dgst = ref
|
||||||
|
}
|
||||||
|
case Blobs:
|
||||||
|
dgst = segments[len(segments)-1]
|
||||||
|
case BlobsUploadsSession:
|
||||||
|
if segments[len(segments)-1] != APIPartUpload && segments[len(segments)-2] == APIPartUpload {
|
||||||
|
image = strings.Join(segments[3:len(segments)-3], "/")
|
||||||
|
ref = segments[len(segments)-1]
|
||||||
|
}
|
||||||
|
if _, ok := paramMap["digest"]; ok {
|
||||||
|
dgst = paramMap["digest"]
|
||||||
|
}
|
||||||
|
case Tags:
|
||||||
|
// do nothing.
|
||||||
|
case Referrers:
|
||||||
|
dgst = segments[len(segments)-1]
|
||||||
|
case Invalid:
|
||||||
|
log.Warn().Msgf("Invalid route: %s", path)
|
||||||
|
default:
|
||||||
|
log.Warn().Msgf("Unknown route type: %s", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msgf(
|
||||||
|
"For path: %s, rootIdentifier: %s, registry: %s, image: %s, ref: %s, dgst: %s, tag: %s",
|
||||||
|
path, rootIdentifier, registry, image, ref, dgst, tag,
|
||||||
|
)
|
||||||
|
|
||||||
|
return rootIdentifier, registry, image, ref, dgst, tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleErrors(ctx context.Context, errors errcode.Errors, w http.ResponseWriter) {
|
||||||
|
if !commons.IsEmpty(errors) {
|
||||||
|
_ = errcode.ServeJSON(w, errors)
|
||||||
|
docker.LogError(errors)
|
||||||
|
log.Ctx(ctx).Error().Errs("OCI errors", errors).Msgf("Error occurred")
|
||||||
|
} else if status, ok := ctx.Value("http.response.status").(int); ok && status >= 200 && status <= 399 {
|
||||||
|
dcontext.GetResponseLogger(ctx, log.Info()).Msg("response completed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) getRegistryInfo(r *http.Request, remoteSupport bool) (pkg.RegistryInfo, error) {
|
||||||
|
ctx := r.Context()
|
||||||
|
queryParams := r.URL.Query()
|
||||||
|
path := r.URL.Path
|
||||||
|
paramMap := GetQueryParamMap(queryParams)
|
||||||
|
rootIdentifier, registryIdentifier, image, ref, dgst, tag := ExtractPathVars(path, paramMap)
|
||||||
|
if err := metadata.ValidateIdentifier(rootIdentifier); err != nil {
|
||||||
|
return pkg.RegistryInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rootSpace, err := h.SpaceStore.FindByRef(ctx, rootIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Error().Msgf("Root space not found: %s", rootIdentifier)
|
||||||
|
return pkg.RegistryInfo{}, errcode.ErrCodeRootNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
registry, err := h.Controller.RegistryDao.GetByRootParentIDAndName(ctx, rootSpace.ID, registryIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Error().Msgf(
|
||||||
|
"registry %s not found for root: %s. Reason: %s", registryIdentifier, rootSpace.Identifier, err,
|
||||||
|
)
|
||||||
|
return pkg.RegistryInfo{}, errcode.ErrCodeRegNotFound
|
||||||
|
}
|
||||||
|
_, err = h.SpaceStore.Find(r.Context(), registry.ParentID)
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Error().Msgf("Parent space not found: %d", registry.ParentID)
|
||||||
|
return pkg.RegistryInfo{}, errcode.ErrCodeParentNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
info := &pkg.RegistryInfo{
|
||||||
|
ArtifactInfo: &pkg.ArtifactInfo{
|
||||||
|
BaseInfo: &pkg.BaseInfo{
|
||||||
|
RootIdentifier: rootIdentifier,
|
||||||
|
RootParentID: rootSpace.ID,
|
||||||
|
ParentID: registry.ParentID,
|
||||||
|
},
|
||||||
|
RegIdentifier: registryIdentifier,
|
||||||
|
Image: image,
|
||||||
|
},
|
||||||
|
Reference: ref,
|
||||||
|
Digest: dgst,
|
||||||
|
Tag: tag,
|
||||||
|
URLBuilder: v2.NewURLBuilderFromRequest(r, false),
|
||||||
|
Path: r.URL.Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Ctx(ctx).Info().Msgf("Dispatch: URI: %s", path)
|
||||||
|
if commons.IsEmpty(rootSpace.Identifier) {
|
||||||
|
log.Ctx(ctx).Error().Msgf("ParentRef not found in context")
|
||||||
|
return pkg.RegistryInfo{}, errcode.ErrCodeParentNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if commons.IsEmpty(registryIdentifier) {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("registry not found in context")
|
||||||
|
return pkg.RegistryInfo{}, errcode.ErrCodeRegNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if !commons.IsEmpty(info.Image) && !commons.IsEmpty(info.Tag) {
|
||||||
|
flag, err2 := MatchArtifactFilter(registry.AllowedPattern, registry.BlockedPattern, info.Image+":"+info.Tag)
|
||||||
|
if !flag || err2 != nil {
|
||||||
|
return pkg.RegistryInfo{}, errcode.ErrCodeDenied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if registry.Type == artifact.RegistryTypeUPSTREAM && !remoteSupport {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("Remote registryIdentifier %s not supported", registryIdentifier)
|
||||||
|
return pkg.RegistryInfo{}, errcode.ErrCodeDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
return *info, nil
|
||||||
|
}
|
35
registry/app/api/handler/oci/delete_blob.go
Normal file
35
registry/app/api/handler/oci/delete_blob.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) DeleteBlob(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
headers, errs := h.Controller.DeleteBlob(r.Context(), info)
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), errs, w)
|
||||||
|
}
|
35
registry/app/api/handler/oci/delete_blob_upload.go
Normal file
35
registry/app/api/handler/oci/delete_blob_upload.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) CancelBlobUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
headers, errs := h.Controller.CancelBlobUpload(r.Context(), info, r.FormValue("_state"))
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), errs, w)
|
||||||
|
}
|
47
registry/app/api/handler/oci/delete_manifest.go
Normal file
47
registry/app/api/handler/oci/delete_manifest.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PutManifest validates and stores a manifest in the registry.
|
||||||
|
func (h *Handler) DeleteManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
length := r.ContentLength
|
||||||
|
if length > 0 {
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, length)
|
||||||
|
}
|
||||||
|
errs, headers := h.Controller.DeleteManifest(r.Context(), info)
|
||||||
|
|
||||||
|
if !commons.IsEmpty(errs) {
|
||||||
|
log.Ctx(ctx).Error().Msgf("DeleteManifest: %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if headers != nil {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(ctx, errs, w)
|
||||||
|
}
|
23
registry/app/api/handler/oci/get_base.go
Normal file
23
registry/app/api/handler/oci/get_base.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) APIBase(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
91
registry/app/api/handler/oci/get_blob.go
Normal file
91
registry/app/api/handler/oci/get_blob.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) GetBlob(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
info, err := h.getRegistryInfo(r, true)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result := h.Controller.GetBlob(ctx, info)
|
||||||
|
|
||||||
|
response, ok := result.(*docker.GetBlobResponse)
|
||||||
|
if !ok {
|
||||||
|
log.Ctx(ctx).Error().Msg("Failed to cast result to GetBlobResponse")
|
||||||
|
handleErrors(ctx, []error{errors.New("failed to cast result to GetBlobResponse")}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if response.Body != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
if response.ReadCloser != nil {
|
||||||
|
response.ReadCloser.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if commons.IsEmpty(response.GetErrors()) {
|
||||||
|
if !commons.IsEmpty(response.RedirectURL) {
|
||||||
|
http.Redirect(w, r, response.RedirectURL, http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.ResponseHeaders.WriteHeadersToResponse(w)
|
||||||
|
if r.Method == http.MethodHead {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.serveContent(w, r, response, info)
|
||||||
|
response.ResponseHeaders.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleErrors(r.Context(), response.GetErrors(), w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) serveContent(
|
||||||
|
w http.ResponseWriter, r *http.Request, response *docker.GetBlobResponse, info pkg.RegistryInfo,
|
||||||
|
) {
|
||||||
|
if response.Body != nil {
|
||||||
|
http.ServeContent(w, r, info.Digest, time.Time{}, response.Body)
|
||||||
|
} else {
|
||||||
|
// Use io.CopyN to avoid out of memory when pulling big blob
|
||||||
|
written, err2 := io.CopyN(w, response.ReadCloser, response.Size)
|
||||||
|
if err2 != nil {
|
||||||
|
response.Errors = append(response.Errors, errors.New("error copying blob to response"))
|
||||||
|
log.Ctx(r.Context()).Error().Msg("error copying blob to response:")
|
||||||
|
}
|
||||||
|
if written != response.Size {
|
||||||
|
response.Errors = append(
|
||||||
|
response.Errors,
|
||||||
|
fmt.Errorf(fmt.Sprintf("The size mismatch, actual:%d, expected: %d", written, response.Size)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
registry/app/api/handler/oci/get_blob_upload.go
Normal file
37
registry/app/api/handler/oci/get_blob_upload.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) GetUploadBlobStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stateToken := r.FormValue("_state")
|
||||||
|
headers, errs := h.Controller.GetUploadBlobStatus(r.Context(), info, stateToken)
|
||||||
|
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), errs, w)
|
||||||
|
}
|
21
registry/app/api/handler/oci/get_catalog.go
Normal file
21
registry/app/api/handler/oci/get_catalog.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func (h *Handler) GetCatalog(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.Controller.GetCatalog(w, r)
|
||||||
|
}
|
57
registry/app/api/handler/oci/get_manifest.go
Normal file
57
registry/app/api/handler/oci/get_manifest.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/dist_temp/errcode"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetManifest fetches the image manifest from the storage backend, if it exists.
|
||||||
|
func (h *Handler) GetManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
info, err := h.getRegistryInfo(r, true)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(ctx, errcode.Errors{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result := h.Controller.PullManifest(
|
||||||
|
ctx,
|
||||||
|
info,
|
||||||
|
r.Header[commons.HeaderAccept],
|
||||||
|
r.Header[commons.HeaderIfNoneMatch],
|
||||||
|
)
|
||||||
|
if commons.IsEmpty(result.GetErrors()) {
|
||||||
|
response, ok := result.(*docker.GetManifestResponse)
|
||||||
|
if !ok {
|
||||||
|
log.Ctx(ctx).Error().Msg("Failed to cast result to GetManifestResponse")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.ResponseHeaders.WriteToResponse(w)
|
||||||
|
_, bytes, _ := response.Manifest.Payload()
|
||||||
|
if _, err := w.Write(bytes); err != nil {
|
||||||
|
log.Ctx(ctx).Error().Err(err).Msg("Failed to write response")
|
||||||
|
response.ResponseHeaders.Code = http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleErrors(ctx, result.GetErrors(), w)
|
||||||
|
}
|
45
registry/app/api/handler/oci/get_referrers.go
Normal file
45
registry/app/api/handler/oci/get_referrers.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/dist_temp/errcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) GetReferrers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer r.Body.Close()
|
||||||
|
errorsList := make(errcode.Errors, 0)
|
||||||
|
|
||||||
|
index, responseHeaders, err := h.Controller.GetReferrers(r.Context(), info, r.URL.Query().Get("artifactType"))
|
||||||
|
if err != nil {
|
||||||
|
errorsList = append(errorsList, err)
|
||||||
|
}
|
||||||
|
if index != nil {
|
||||||
|
responseHeaders.WriteHeadersToResponse(w)
|
||||||
|
if err := json.NewEncoder(w).Encode(index); err != nil {
|
||||||
|
errorsList = append(errorsList, errcode.ErrCodeUnknown.WithDetail(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleErrors(r.Context(), errorsList, w)
|
||||||
|
}
|
68
registry/app/api/handler/oci/get_tags.go
Normal file
68
registry/app/api/handler/oci/get_tags.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/dist_temp/errcode"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) GetTags(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(ctx, []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errorsList := make(errcode.Errors, 0)
|
||||||
|
|
||||||
|
q := r.URL.Query()
|
||||||
|
lastEntry := q.Get("last")
|
||||||
|
maxEntries, err := strconv.Atoi(q.Get("n"))
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Error().Err(err).Msgf("Failed to parse max entries %s", q.Get("n"))
|
||||||
|
maxEntries = docker.DefaultMaximumReturnedEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxEntries <= 0 {
|
||||||
|
maxEntries = docker.DefaultMaximumReturnedEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
rs, tags, err := h.Controller.GetTags(ctx, lastEntry, maxEntries, r.URL.String(), info)
|
||||||
|
log.Ctx(ctx).Debug().Msgf("GetTags: %v %s", rs, tags)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Error().Err(err).Msg("Failed to list tags")
|
||||||
|
handleErrors(ctx, errorsList, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rs.WriteHeadersToResponse(w)
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
if err := enc.Encode(
|
||||||
|
docker.TagsAPIResponse{
|
||||||
|
Name: info.RegIdentifier,
|
||||||
|
Tags: tags,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
errorsList = append(errorsList, errcode.ErrCodeUnknown.WithDetail(err))
|
||||||
|
}
|
||||||
|
handleErrors(ctx, errorsList, w)
|
||||||
|
}
|
212
registry/app/api/handler/oci/get_token.go
Normal file
212
registry/app/api/handler/oci/get_token.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/app/api/auth"
|
||||||
|
"github.com/harness/gitness/app/api/render"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/app/jwt"
|
||||||
|
"github.com/harness/gitness/app/paths"
|
||||||
|
"github.com/harness/gitness/app/token"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, ok := request.AuthSessionFrom(ctx)
|
||||||
|
if !ok || session.Principal == auth.AnonymousPrincipal {
|
||||||
|
returnForbiddenResponse(w, fmt.Errorf("no auth session found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tokenMetadata, okt := session.Metadata.(*auth.TokenMetadata); okt &&
|
||||||
|
tokenMetadata.TokenType != enum.TokenTypePAT {
|
||||||
|
returnForbiddenResponse(w, fmt.Errorf("only personal access token allowed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := h.UserCtrl.FindNoAuth(ctx, session.Principal.UID)
|
||||||
|
if err != nil {
|
||||||
|
returnForbiddenResponse(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
requestedOciAccess := GetRequestedResourceActions(getScopes(r.URL))
|
||||||
|
var accessPermissionsList = []jwt.AccessPermissions{}
|
||||||
|
for _, ra := range requestedOciAccess {
|
||||||
|
space, err := h.getSpace(ctx, ra.Name)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(ctx, w, err)
|
||||||
|
log.Ctx(ctx).Warn().Msgf("failed to find space by ref: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
accessPermissionsList = h.getAccessPermissionList(ctx, space, ra, session, accessPermissionsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
subClaimsAccessPermissions := &jwt.SubClaimsAccessPermissions{
|
||||||
|
Source: jwt.OciSource,
|
||||||
|
Permissions: accessPermissionsList,
|
||||||
|
}
|
||||||
|
|
||||||
|
jwtToken, err := h.getTokenDetails(user, subClaimsAccessPermissions)
|
||||||
|
if err != nil {
|
||||||
|
returnForbiddenResponse(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if jwtToken != "" {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := w.Write([]byte(fmt.Sprintf("{\"token\":\"%s\"}", jwtToken)))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msgf("failed to write token response: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) getSpace(ctx context.Context, name string) (*types.Space, error) {
|
||||||
|
spaceRef, _, _ := paths.DisectRoot(name)
|
||||||
|
space, err := h.SpaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
return space, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) getAccessPermissionList(
|
||||||
|
ctx context.Context, space *types.Space, ra *ResourceActions, session *auth.Session,
|
||||||
|
accessPermissionsList []jwt.AccessPermissions,
|
||||||
|
) []jwt.AccessPermissions {
|
||||||
|
accessPermissions := &jwt.AccessPermissions{SpaceID: space.ID, Permissions: []enum.Permission{}}
|
||||||
|
|
||||||
|
for _, a := range ra.Actions {
|
||||||
|
permission, err := getPermissionFromAction(ctx, a)
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("failed to get permission from action: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
scopeErr := apiauth.CheckSpaceScope(
|
||||||
|
ctx,
|
||||||
|
h.Authorizer,
|
||||||
|
session,
|
||||||
|
space,
|
||||||
|
enum.ResourceTypeRegistry,
|
||||||
|
permission,
|
||||||
|
)
|
||||||
|
if scopeErr != nil {
|
||||||
|
log.Ctx(ctx).Warn().Msgf("failed to check space scope: %v", scopeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
accessPermissions.Permissions = append(accessPermissions.Permissions, permission)
|
||||||
|
}
|
||||||
|
accessPermissionsList = append(accessPermissionsList, *accessPermissions)
|
||||||
|
return accessPermissionsList
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPermissionFromAction(ctx context.Context, action string) (enum.Permission, error) {
|
||||||
|
switch action {
|
||||||
|
case "pull":
|
||||||
|
return enum.PermissionArtifactsDownload, nil
|
||||||
|
case "push":
|
||||||
|
return enum.PermissionArtifactsUpload, nil
|
||||||
|
case "delete":
|
||||||
|
return enum.PermissionArtifactsDelete, nil
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("unknown action: %s", action)
|
||||||
|
log.Ctx(ctx).Err(err).Msgf("Failed to get permission from action: %v", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func returnForbiddenResponse(w http.ResponseWriter, err error) {
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
_, err2 := w.Write([]byte(fmt.Sprintf("requested access to the resource is denied: %v", err)))
|
||||||
|
if err2 != nil {
|
||||||
|
log.Error().Msgf("failed to write token response: %v", err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getTokenDetails attempts to get token details.
|
||||||
|
*/
|
||||||
|
func (h *Handler) getTokenDetails(
|
||||||
|
user *types.User,
|
||||||
|
accessPermissions *jwt.SubClaimsAccessPermissions,
|
||||||
|
) (string, error) {
|
||||||
|
return token.CreateUserWithAccessPermissions(user, accessPermissions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequestedResourceActions ...
|
||||||
|
func GetRequestedResourceActions(scopes []string) []*ResourceActions {
|
||||||
|
var res []*ResourceActions
|
||||||
|
for _, s := range scopes {
|
||||||
|
if s == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items := strings.Split(s, ":")
|
||||||
|
length := len(items)
|
||||||
|
|
||||||
|
var resourceType string
|
||||||
|
var resourceName string
|
||||||
|
actions := make([]string, 0)
|
||||||
|
|
||||||
|
switch length {
|
||||||
|
case 1:
|
||||||
|
resourceType = items[0]
|
||||||
|
case 2:
|
||||||
|
resourceType = items[0]
|
||||||
|
resourceName = items[1]
|
||||||
|
default:
|
||||||
|
resourceType = items[0]
|
||||||
|
resourceName = strings.Join(items[1:length-1], ":")
|
||||||
|
if len(items[length-1]) > 0 {
|
||||||
|
actions = strings.Split(items[length-1], ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(
|
||||||
|
res, &ResourceActions{
|
||||||
|
Type: resourceType,
|
||||||
|
Name: resourceName,
|
||||||
|
Actions: actions,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func getScopes(u *url.URL) []string {
|
||||||
|
var sector string
|
||||||
|
var result []string
|
||||||
|
for _, sector = range u.Query()["scope"] {
|
||||||
|
result = append(result, strings.Split(sector, " ")...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceActions stores allowed actions on a resource.
|
||||||
|
type ResourceActions struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Actions []string `json:"actions"`
|
||||||
|
}
|
48
registry/app/api/handler/oci/head_blob.go
Normal file
48
registry/app/api/handler/oci/head_blob.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) HeadBlob(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
headers, body, _, readCloser, redirectURL, errs := h.Controller.HeadBlob(r.Context(), info)
|
||||||
|
defer func() {
|
||||||
|
if body != nil {
|
||||||
|
body.Close()
|
||||||
|
}
|
||||||
|
if readCloser != nil {
|
||||||
|
readCloser.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
if redirectURL != "" {
|
||||||
|
http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.WriteHeadersToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), errs, w)
|
||||||
|
}
|
43
registry/app/api/handler/oci/head_manifest.go
Normal file
43
registry/app/api/handler/oci/head_manifest.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/dist_temp/errcode"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HeadManifest fetches the image manifest from the storage backend, if it exists.
|
||||||
|
func (h *Handler) HeadManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, true)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), errcode.Errors{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result := h.Controller.HeadManifest(
|
||||||
|
r.Context(),
|
||||||
|
info,
|
||||||
|
r.Header[commons.HeaderAccept],
|
||||||
|
r.Header[commons.HeaderIfNoneMatch],
|
||||||
|
)
|
||||||
|
if commons.IsEmpty(result.GetErrors()) {
|
||||||
|
result.(*docker.GetManifestResponse).ResponseHeaders.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), result.GetErrors(), w)
|
||||||
|
}
|
43
registry/app/api/handler/oci/patch_blob_upload.go
Normal file
43
registry/app/api/handler/oci/patch_blob_upload.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) PatchBlobUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ct := r.Header.Get(commons.HeaderContentType)
|
||||||
|
cr := r.Header.Get(commons.HeaderContentRange)
|
||||||
|
cl := r.Header.Get(commons.HeaderContentLength)
|
||||||
|
length := r.ContentLength
|
||||||
|
if length > 0 {
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, length)
|
||||||
|
}
|
||||||
|
stateToken := r.FormValue("_state")
|
||||||
|
headers, errs := h.Controller.PatchBlobUpload(r.Context(), info, ct, cr, cl, length, stateToken, r.Body)
|
||||||
|
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), errs, w)
|
||||||
|
}
|
42
registry/app/api/handler/oci/post_blob_upload.go
Normal file
42
registry/app/api/handler/oci/post_blob_upload.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) InitiateUploadBlob(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fromParam := r.FormValue("from")
|
||||||
|
fromParamParts := strings.Split(fromParam, "/")
|
||||||
|
fromRepo := ""
|
||||||
|
if len(fromParamParts) > 1 {
|
||||||
|
fromRepo = fromParamParts[1]
|
||||||
|
}
|
||||||
|
mountDigest := r.FormValue("mount")
|
||||||
|
headers, errs := h.Controller.InitiateUploadBlob(r.Context(), info, fromRepo, mountDigest)
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), errs, w)
|
||||||
|
}
|
41
registry/app/api/handler/oci/put_blob_upload.go
Normal file
41
registry/app/api/handler/oci/put_blob_upload.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) CompleteBlobUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stateToken := r.FormValue("_state")
|
||||||
|
length := r.ContentLength
|
||||||
|
if length > 0 {
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
headers, errs := h.Controller.CompleteBlobUpload(r.Context(), info, r.Body, r.ContentLength, stateToken)
|
||||||
|
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(r.Context(), errs, w)
|
||||||
|
}
|
50
registry/app/api/handler/oci/put_manifest.go
Normal file
50
registry/app/api/handler/oci/put_manifest.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxManifestBodySize = 4 * 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// PutManifest validates and stores a manifest in the registry.
|
||||||
|
func (h *Handler) PutManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
info, err := h.getRegistryInfo(r, false)
|
||||||
|
if err != nil {
|
||||||
|
handleErrors(r.Context(), []error{err}, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mediaType := r.Header.Get("Content-Type")
|
||||||
|
length := r.ContentLength
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, maxManifestBodySize)
|
||||||
|
|
||||||
|
headers, errs := h.Controller.PutManifest(r.Context(), info, mediaType, r.Body, length)
|
||||||
|
if !commons.IsEmpty(errs) {
|
||||||
|
log.Ctx(ctx).Error().Errs("Failed to Put manifest", errs).Msg("Failed to Put manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
if commons.IsEmpty(errs) {
|
||||||
|
headers.WriteToResponse(w)
|
||||||
|
}
|
||||||
|
handleErrors(ctx, errs, w)
|
||||||
|
}
|
63
registry/app/api/handler/swagger/swagger.go
Normal file
63
registry/app/api/handler/swagger/swagger.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package swagger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
httpswagger "github.com/swaggo/http-swagger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSwaggerHandler(base string) Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
// Generate OpenAPI specification
|
||||||
|
swagger, err := artifact.GetSwagger()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the OpenAPI specification JSON
|
||||||
|
r.Get(
|
||||||
|
fmt.Sprintf("%s/swagger.json", base), http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
jsonResponse, _ := json.Marshal(swagger)
|
||||||
|
_, err2 := w.Write(jsonResponse)
|
||||||
|
if err2 != nil {
|
||||||
|
log.Error().Err(err2).Msg("Failed to write response")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
r.Get(
|
||||||
|
fmt.Sprintf("%s/swagger/*", base), httpswagger.Handler(
|
||||||
|
httpswagger.URL(fmt.Sprintf("%s/swagger.json", base)), // The url pointing to API definition
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
121
registry/app/api/middleware/auth.go
Normal file
121
registry/app/api/middleware/auth.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/render"
|
||||||
|
"github.com/harness/gitness/app/api/request"
|
||||||
|
"github.com/harness/gitness/app/auth"
|
||||||
|
"github.com/harness/gitness/app/jwt"
|
||||||
|
"github.com/harness/gitness/registry/app/api/handler/oci"
|
||||||
|
registryauth "github.com/harness/gitness/registry/app/auth"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OciCheckAuth(url string) func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
if session.Principal == auth.AnonymousPrincipal {
|
||||||
|
scope := getScope(r)
|
||||||
|
returnUnauthorised(ctx, w, url, scope)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockNonOciSourceToken blocks any request that doesn't have AccessPermissionMetadata.
|
||||||
|
func BlockNonOciSourceToken(url string) func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
if session, oks := request.AuthSessionFrom(ctx); oks {
|
||||||
|
if metadata, okt := session.Metadata.(*auth.AccessPermissionMetadata); !okt ||
|
||||||
|
metadata.AccessPermissions.Source != jwt.OciSource {
|
||||||
|
log.Ctx(ctx).Warn().
|
||||||
|
Msg("blocking request - non OCI source tokens are not allowed for usage with oci endpoints")
|
||||||
|
|
||||||
|
scope := getScope(r)
|
||||||
|
returnUnauthorised(ctx, w, url, scope)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckAuth() func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
if session.Principal == auth.AnonymousPrincipal {
|
||||||
|
render.Unauthorized(ctx, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRefsFromName(name string) (spaceRef, repoRef string) {
|
||||||
|
name = strings.Trim(name, "/")
|
||||||
|
refs := strings.Split(name, "/")
|
||||||
|
spaceRef = refs[0]
|
||||||
|
repoRef = refs[1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getScope(r *http.Request) string {
|
||||||
|
var scope string
|
||||||
|
path := r.URL.Path
|
||||||
|
if path != "/v2/" && path != "/v2/token" {
|
||||||
|
paramMap := oci.GetQueryParamMap(r.URL.Query())
|
||||||
|
rootIdentifier, registryIdentifier, _, _, _, _ := oci.ExtractPathVars(path, paramMap)
|
||||||
|
var access []registryauth.Access
|
||||||
|
access = registryauth.AppendAccess(access, r.Method, rootIdentifier, registryIdentifier)
|
||||||
|
if fromRepo := r.FormValue("from"); fromRepo != "" {
|
||||||
|
space, repoName := getRefsFromName(fromRepo)
|
||||||
|
access = registryauth.AppendAccess(access, http.MethodGet, space, repoName)
|
||||||
|
}
|
||||||
|
scope = registryauth.NewAccessSet(access...).ScopeParam()
|
||||||
|
}
|
||||||
|
return scope
|
||||||
|
}
|
||||||
|
|
||||||
|
func returnUnauthorised(ctx context.Context, w http.ResponseWriter, url string, scope string) {
|
||||||
|
header := fmt.Sprintf(`Bearer realm="%s", service="gitness-registry"`, url)
|
||||||
|
if scope != "" {
|
||||||
|
header = fmt.Sprintf(`%s, scope="%s"`, header, scope)
|
||||||
|
}
|
||||||
|
w.Header().Set("WWW-Authenticate", header)
|
||||||
|
render.Unauthorized(ctx, w)
|
||||||
|
}
|
1607
registry/app/api/openapi/api.yaml
Normal file
1607
registry/app/api/openapi/api.yaml
Normal file
File diff suppressed because it is too large
Load Diff
3803
registry/app/api/openapi/contracts/artifact/services.gen.go
Normal file
3803
registry/app/api/openapi/contracts/artifact/services.gen.go
Normal file
File diff suppressed because it is too large
Load Diff
977
registry/app/api/openapi/contracts/artifact/types.gen.go
Normal file
977
registry/app/api/openapi/contracts/artifact/types.gen.go
Normal file
@ -0,0 +1,977 @@
|
|||||||
|
// Package artifact provides primitives to interact with the openapi HTTP API.
|
||||||
|
//
|
||||||
|
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT.
|
||||||
|
package artifact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/oapi-codegen/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for AuthType.
|
||||||
|
const (
|
||||||
|
AuthTypeAnonymous AuthType = "Anonymous"
|
||||||
|
AuthTypeUserPassword AuthType = "UserPassword"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for ClientSetupStepType.
|
||||||
|
const (
|
||||||
|
ClientSetupStepTypeGenerateToken ClientSetupStepType = "GenerateToken"
|
||||||
|
ClientSetupStepTypeStatic ClientSetupStepType = "Static"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for PackageType.
|
||||||
|
const (
|
||||||
|
PackageTypeDOCKER PackageType = "DOCKER"
|
||||||
|
PackageTypeGENERIC PackageType = "GENERIC"
|
||||||
|
PackageTypeHELM PackageType = "HELM"
|
||||||
|
PackageTypeMAVEN PackageType = "MAVEN"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for RegistryType.
|
||||||
|
const (
|
||||||
|
RegistryTypeUPSTREAM RegistryType = "UPSTREAM"
|
||||||
|
RegistryTypeVIRTUAL RegistryType = "VIRTUAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for Status.
|
||||||
|
const (
|
||||||
|
StatusERROR Status = "ERROR"
|
||||||
|
StatusFAILURE Status = "FAILURE"
|
||||||
|
StatusSUCCESS Status = "SUCCESS"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for UpstreamConfigSource.
|
||||||
|
const (
|
||||||
|
UpstreamConfigSourceCustom UpstreamConfigSource = "Custom"
|
||||||
|
UpstreamConfigSourceDockerhub UpstreamConfigSource = "Dockerhub"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for RegistryTypeParam.
|
||||||
|
const (
|
||||||
|
UPSTREAM RegistryTypeParam = "UPSTREAM"
|
||||||
|
VIRTUAL RegistryTypeParam = "VIRTUAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for GetAllRegistriesParamsType.
|
||||||
|
const (
|
||||||
|
GetAllRegistriesParamsTypeUPSTREAM GetAllRegistriesParamsType = "UPSTREAM"
|
||||||
|
GetAllRegistriesParamsTypeVIRTUAL GetAllRegistriesParamsType = "VIRTUAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Anonymous defines model for Anonymous.
|
||||||
|
type Anonymous interface{}
|
||||||
|
|
||||||
|
// ArtifactLabelRequest defines model for ArtifactLabelRequest.
|
||||||
|
type ArtifactLabelRequest struct {
|
||||||
|
Labels []string `json:"labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactMetadata Artifact Metadata
|
||||||
|
type ArtifactMetadata struct {
|
||||||
|
DownloadsCount *int64 `json:"downloadsCount,omitempty"`
|
||||||
|
Labels *[]string `json:"labels,omitempty"`
|
||||||
|
LastModified *string `json:"lastModified,omitempty"`
|
||||||
|
LatestVersion string `json:"latestVersion"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType *PackageType `json:"packageType,omitempty"`
|
||||||
|
RegistryIdentifier string `json:"registryIdentifier"`
|
||||||
|
RegistryPath string `json:"registryPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactStats Harness Artifact Stats
|
||||||
|
type ArtifactStats struct {
|
||||||
|
DownloadCount *int64 `json:"downloadCount,omitempty"`
|
||||||
|
DownloadSize *int64 `json:"downloadSize,omitempty"`
|
||||||
|
TotalStorageSize *int64 `json:"totalStorageSize,omitempty"`
|
||||||
|
UploadSize *int64 `json:"uploadSize,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactSummary Harness Artifact Summary
|
||||||
|
type ArtifactSummary struct {
|
||||||
|
CreatedAt *string `json:"createdAt,omitempty"`
|
||||||
|
DownloadsCount *int64 `json:"downloadsCount,omitempty"`
|
||||||
|
ImageName string `json:"imageName"`
|
||||||
|
Labels *[]string `json:"labels,omitempty"`
|
||||||
|
ModifiedAt *string `json:"modifiedAt,omitempty"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType PackageType `json:"packageType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactVersionMetadata Artifact Version Metadata
|
||||||
|
type ArtifactVersionMetadata struct {
|
||||||
|
DigestCount *int64 `json:"digestCount,omitempty"`
|
||||||
|
DownloadsCount *int64 `json:"downloadsCount,omitempty"`
|
||||||
|
IslatestVersion *bool `json:"islatestVersion,omitempty"`
|
||||||
|
LastModified *string `json:"lastModified,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType *PackageType `json:"packageType,omitempty"`
|
||||||
|
PullCommand *string `json:"pullCommand,omitempty"`
|
||||||
|
RegistryIdentifier string `json:"registryIdentifier"`
|
||||||
|
RegistryPath string `json:"registryPath"`
|
||||||
|
Size *string `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactVersionSummary Docker Artifact Version Summary
|
||||||
|
type ArtifactVersionSummary struct {
|
||||||
|
ImageName string `json:"imageName"`
|
||||||
|
IsLatestVersion *bool `json:"isLatestVersion,omitempty"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType PackageType `json:"packageType"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthType Authentication type
|
||||||
|
type AuthType string
|
||||||
|
|
||||||
|
// CleanupPolicy Cleanup Policy for Harness Artifact Registries
|
||||||
|
type CleanupPolicy struct {
|
||||||
|
ExpireDays *int `json:"expireDays,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
PackagePrefix *[]string `json:"packagePrefix,omitempty"`
|
||||||
|
VersionPrefix *[]string `json:"versionPrefix,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientSetupDetails Client Setup Details
|
||||||
|
type ClientSetupDetails struct {
|
||||||
|
MainHeader string `json:"mainHeader"`
|
||||||
|
SecHeader string `json:"secHeader"`
|
||||||
|
Sections []ClientSetupSection `json:"sections"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientSetupSection Client Setup Section
|
||||||
|
type ClientSetupSection struct {
|
||||||
|
Header *string `json:"header,omitempty"`
|
||||||
|
Steps *[]ClientSetupStep `json:"steps,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientSetupStep Client Setup Step
|
||||||
|
type ClientSetupStep struct {
|
||||||
|
Commands *[]string `json:"commands,omitempty"`
|
||||||
|
Header *string `json:"header,omitempty"`
|
||||||
|
|
||||||
|
// Type ClientSetupStepType type
|
||||||
|
Type *ClientSetupStepType `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientSetupStepType ClientSetupStepType type
|
||||||
|
type ClientSetupStepType string
|
||||||
|
|
||||||
|
// DockerArtifactDetail Docker Artifact Detail
|
||||||
|
type DockerArtifactDetail struct {
|
||||||
|
CreatedAt *string `json:"createdAt,omitempty"`
|
||||||
|
DownloadsCount *int64 `json:"downloadsCount,omitempty"`
|
||||||
|
ImageName string `json:"imageName"`
|
||||||
|
IsLatestVersion *bool `json:"isLatestVersion,omitempty"`
|
||||||
|
ModifiedAt *string `json:"modifiedAt,omitempty"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType PackageType `json:"packageType"`
|
||||||
|
PullCommand *string `json:"pullCommand,omitempty"`
|
||||||
|
RegistryPath string `json:"registryPath"`
|
||||||
|
Size *string `json:"size,omitempty"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerArtifactManifest Docker Artifact Manifest
|
||||||
|
type DockerArtifactManifest struct {
|
||||||
|
Manifest string `json:"manifest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerLayerEntry Harness Artifact Layers
|
||||||
|
type DockerLayerEntry struct {
|
||||||
|
Command string `json:"command"`
|
||||||
|
Size *string `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerLayersSummary Harness Layers Summary
|
||||||
|
type DockerLayersSummary struct {
|
||||||
|
Digest string `json:"digest"`
|
||||||
|
Layers *[]DockerLayerEntry `json:"layers,omitempty"`
|
||||||
|
OsArch *string `json:"osArch,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerManifestDetails Harness Artifact Layers
|
||||||
|
type DockerManifestDetails struct {
|
||||||
|
CreatedAt *string `json:"createdAt,omitempty"`
|
||||||
|
Digest string `json:"digest"`
|
||||||
|
OsArch string `json:"osArch"`
|
||||||
|
Size *string `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerManifests Harness Manifests
|
||||||
|
type DockerManifests struct {
|
||||||
|
ImageName string `json:"imageName"`
|
||||||
|
IsLatestVersion *bool `json:"isLatestVersion,omitempty"`
|
||||||
|
Manifests *[]DockerManifestDetails `json:"manifests,omitempty"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error defines model for Error.
|
||||||
|
type Error struct {
|
||||||
|
// Code The http error code
|
||||||
|
Code string `json:"code"`
|
||||||
|
|
||||||
|
// Details Additional details about the error
|
||||||
|
Details *map[string]interface{} `json:"details,omitempty"`
|
||||||
|
|
||||||
|
// Message The reason the request failed
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelmArtifactDetail Helm Artifact Detail
|
||||||
|
type HelmArtifactDetail struct {
|
||||||
|
Artifact *string `json:"artifact,omitempty"`
|
||||||
|
CreatedAt *string `json:"createdAt,omitempty"`
|
||||||
|
DownloadsCount *int64 `json:"downloadsCount,omitempty"`
|
||||||
|
IsLatestVersion *bool `json:"isLatestVersion,omitempty"`
|
||||||
|
ModifiedAt *string `json:"modifiedAt,omitempty"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType PackageType `json:"packageType"`
|
||||||
|
PullCommand *string `json:"pullCommand,omitempty"`
|
||||||
|
RegistryPath string `json:"registryPath"`
|
||||||
|
Size *string `json:"size,omitempty"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelmArtifactManifest Helm Artifact Manifest
|
||||||
|
type HelmArtifactManifest struct {
|
||||||
|
Manifest string `json:"manifest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListArtifact A list of Artifacts
|
||||||
|
type ListArtifact struct {
|
||||||
|
// Artifacts A list of Artifact
|
||||||
|
Artifacts []ArtifactMetadata `json:"artifacts"`
|
||||||
|
|
||||||
|
// ItemCount The total number of items
|
||||||
|
ItemCount *int64 `json:"itemCount,omitempty"`
|
||||||
|
|
||||||
|
// PageCount The total number of pages
|
||||||
|
PageCount *int64 `json:"pageCount,omitempty"`
|
||||||
|
|
||||||
|
// PageIndex The current page
|
||||||
|
PageIndex *int64 `json:"pageIndex,omitempty"`
|
||||||
|
|
||||||
|
// PageSize The number of items per page
|
||||||
|
PageSize *int `json:"pageSize,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListArtifactLabel A list of Harness Artifact Labels
|
||||||
|
type ListArtifactLabel struct {
|
||||||
|
// ItemCount The total number of items
|
||||||
|
ItemCount *int64 `json:"itemCount,omitempty"`
|
||||||
|
Labels []string `json:"labels"`
|
||||||
|
|
||||||
|
// PageCount The total number of pages
|
||||||
|
PageCount *int64 `json:"pageCount,omitempty"`
|
||||||
|
|
||||||
|
// PageIndex The current page
|
||||||
|
PageIndex *int64 `json:"pageIndex,omitempty"`
|
||||||
|
|
||||||
|
// PageSize The number of items per page
|
||||||
|
PageSize *int `json:"pageSize,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListArtifactVersion A list of Artifact versions
|
||||||
|
type ListArtifactVersion struct {
|
||||||
|
// ArtifactVersions A list of Artifact versions
|
||||||
|
ArtifactVersions *[]ArtifactVersionMetadata `json:"artifactVersions,omitempty"`
|
||||||
|
|
||||||
|
// ItemCount The total number of items
|
||||||
|
ItemCount *int64 `json:"itemCount,omitempty"`
|
||||||
|
|
||||||
|
// PageCount The total number of pages
|
||||||
|
PageCount *int64 `json:"pageCount,omitempty"`
|
||||||
|
|
||||||
|
// PageIndex The current page
|
||||||
|
PageIndex *int64 `json:"pageIndex,omitempty"`
|
||||||
|
|
||||||
|
// PageSize The number of items per page
|
||||||
|
PageSize *int `json:"pageSize,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRegistry A list of Harness Artifact Registries
|
||||||
|
type ListRegistry struct {
|
||||||
|
// ItemCount The total number of items
|
||||||
|
ItemCount *int64 `json:"itemCount,omitempty"`
|
||||||
|
|
||||||
|
// PageCount The total number of pages
|
||||||
|
PageCount *int64 `json:"pageCount,omitempty"`
|
||||||
|
|
||||||
|
// PageIndex The current page
|
||||||
|
PageIndex *int64 `json:"pageIndex,omitempty"`
|
||||||
|
|
||||||
|
// PageSize The number of items per page
|
||||||
|
PageSize *int `json:"pageSize,omitempty"`
|
||||||
|
|
||||||
|
// Registries A list of Harness Artifact Registries
|
||||||
|
Registries []RegistryMetadata `json:"registries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
type PackageType string
|
||||||
|
|
||||||
|
// Registry Harness Artifact Registry
|
||||||
|
type Registry struct {
|
||||||
|
AllowedPattern *[]string `json:"allowedPattern,omitempty"`
|
||||||
|
BlockedPattern *[]string `json:"blockedPattern,omitempty"`
|
||||||
|
CleanupPolicy *[]CleanupPolicy `json:"cleanupPolicy,omitempty"`
|
||||||
|
|
||||||
|
// Config SubConfig specific for Virtual or Upstream Registry
|
||||||
|
Config *RegistryConfig `json:"config,omitempty"`
|
||||||
|
CreatedAt *string `json:"createdAt,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
Identifier string `json:"identifier"`
|
||||||
|
Labels *[]string `json:"labels,omitempty"`
|
||||||
|
ModifiedAt *string `json:"modifiedAt,omitempty"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType PackageType `json:"packageType"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryConfig SubConfig specific for Virtual or Upstream Registry
|
||||||
|
type RegistryConfig struct {
|
||||||
|
// Type refers to type of registry i.e virtual or upstream
|
||||||
|
Type RegistryType `json:"type"`
|
||||||
|
union json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryMetadata Harness Artifact Registry Metadata
|
||||||
|
type RegistryMetadata struct {
|
||||||
|
ArtifactsCount *int64 `json:"artifactsCount,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
DownloadsCount *int64 `json:"downloadsCount,omitempty"`
|
||||||
|
Identifier string `json:"identifier"`
|
||||||
|
Labels *[]string `json:"labels,omitempty"`
|
||||||
|
LastModified *string `json:"lastModified,omitempty"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType PackageType `json:"packageType"`
|
||||||
|
Path *string `json:"path,omitempty"`
|
||||||
|
RegistrySize *string `json:"registrySize,omitempty"`
|
||||||
|
|
||||||
|
// Type refers to type of registry i.e virtual or upstream
|
||||||
|
Type RegistryType `json:"type"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryRequest defines model for RegistryRequest.
|
||||||
|
type RegistryRequest struct {
|
||||||
|
AllowedPattern *[]string `json:"allowedPattern,omitempty"`
|
||||||
|
BlockedPattern *[]string `json:"blockedPattern,omitempty"`
|
||||||
|
CleanupPolicy *[]CleanupPolicy `json:"cleanupPolicy,omitempty"`
|
||||||
|
|
||||||
|
// Config SubConfig specific for Virtual or Upstream Registry
|
||||||
|
Config *RegistryConfig `json:"config,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
Identifier string `json:"identifier"`
|
||||||
|
Labels *[]string `json:"labels,omitempty"`
|
||||||
|
|
||||||
|
// PackageType refers to package
|
||||||
|
PackageType PackageType `json:"packageType"`
|
||||||
|
ParentRef *string `json:"parentRef,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryType refers to type of registry i.e virtual or upstream
|
||||||
|
type RegistryType string
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
// UpstreamConfig Configuration for Harness Artifact UpstreamProxies
|
||||||
|
type UpstreamConfig struct {
|
||||||
|
Auth *UpstreamConfig_Auth `json:"auth,omitempty"`
|
||||||
|
|
||||||
|
// AuthType Authentication type
|
||||||
|
AuthType AuthType `json:"authType"`
|
||||||
|
Source *UpstreamConfigSource `json:"source,omitempty"`
|
||||||
|
Url *string `json:"url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpstreamConfig_Auth defines model for UpstreamConfig.Auth.
|
||||||
|
type UpstreamConfig_Auth struct {
|
||||||
|
union json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpstreamConfigSource defines model for UpstreamConfig.Source.
|
||||||
|
type UpstreamConfigSource string
|
||||||
|
|
||||||
|
// UserPassword defines model for UserPassword.
|
||||||
|
type UserPassword struct {
|
||||||
|
SecretIdentifier *string `json:"secretIdentifier,omitempty"`
|
||||||
|
SecretSpaceId *int `json:"secretSpaceId,omitempty"`
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualConfig Configuration for Harness Virtual Artifact Registries
|
||||||
|
type VirtualConfig struct {
|
||||||
|
UpstreamProxies *[]string `json:"upstreamProxies,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelsParam defines model for LabelsParam.
|
||||||
|
type LabelsParam []string
|
||||||
|
|
||||||
|
// RegistryIdentifierParam defines model for RegistryIdentifierParam.
|
||||||
|
type RegistryIdentifierParam string
|
||||||
|
|
||||||
|
// RegistryTypeParam defines model for RegistryTypeParam.
|
||||||
|
type RegistryTypeParam string
|
||||||
|
|
||||||
|
// ArtifactParam defines model for artifactParam.
|
||||||
|
type ArtifactParam string
|
||||||
|
|
||||||
|
// ArtifactPathParam defines model for artifactPathParam.
|
||||||
|
type ArtifactPathParam string
|
||||||
|
|
||||||
|
// DigestParam defines model for digestParam.
|
||||||
|
type DigestParam string
|
||||||
|
|
||||||
|
// FromDateParam defines model for fromDateParam.
|
||||||
|
type FromDateParam string
|
||||||
|
|
||||||
|
// PackageTypeParam defines model for packageTypeParam.
|
||||||
|
type PackageTypeParam []string
|
||||||
|
|
||||||
|
// PageNumber defines model for pageNumber.
|
||||||
|
type PageNumber int64
|
||||||
|
|
||||||
|
// PageSize defines model for pageSize.
|
||||||
|
type PageSize int64
|
||||||
|
|
||||||
|
// RegistryRefPathParam defines model for registryRefPathParam.
|
||||||
|
type RegistryRefPathParam string
|
||||||
|
|
||||||
|
// SearchTerm defines model for searchTerm.
|
||||||
|
type SearchTerm string
|
||||||
|
|
||||||
|
// SortField defines model for sortField.
|
||||||
|
type SortField string
|
||||||
|
|
||||||
|
// SortOrder defines model for sortOrder.
|
||||||
|
type SortOrder string
|
||||||
|
|
||||||
|
// SpaceRefPathParam defines model for spaceRefPathParam.
|
||||||
|
type SpaceRefPathParam string
|
||||||
|
|
||||||
|
// ToDateParam defines model for toDateParam.
|
||||||
|
type ToDateParam string
|
||||||
|
|
||||||
|
// VersionParam defines model for versionParam.
|
||||||
|
type VersionParam string
|
||||||
|
|
||||||
|
// VersionPathParam defines model for versionPathParam.
|
||||||
|
type VersionPathParam string
|
||||||
|
|
||||||
|
// ArtifactLabelResponse defines model for ArtifactLabelResponse.
|
||||||
|
type ArtifactLabelResponse struct {
|
||||||
|
// Data Harness Artifact Summary
|
||||||
|
Data ArtifactSummary `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactStatsResponse defines model for ArtifactStatsResponse.
|
||||||
|
type ArtifactStatsResponse struct {
|
||||||
|
// Data Harness Artifact Stats
|
||||||
|
Data ArtifactStats `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactSummaryResponse defines model for ArtifactSummaryResponse.
|
||||||
|
type ArtifactSummaryResponse struct {
|
||||||
|
// Data Harness Artifact Summary
|
||||||
|
Data ArtifactSummary `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactVersionSummaryResponse defines model for ArtifactVersionSummaryResponse.
|
||||||
|
type ArtifactVersionSummaryResponse struct {
|
||||||
|
// Data Docker Artifact Version Summary
|
||||||
|
Data ArtifactVersionSummary `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadRequest defines model for BadRequest.
|
||||||
|
type BadRequest Error
|
||||||
|
|
||||||
|
// ClientSetupDetailsResponse defines model for ClientSetupDetailsResponse.
|
||||||
|
type ClientSetupDetailsResponse struct {
|
||||||
|
// Data Client Setup Details
|
||||||
|
Data ClientSetupDetails `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerArtifactDetailResponse defines model for DockerArtifactDetailResponse.
|
||||||
|
type DockerArtifactDetailResponse struct {
|
||||||
|
// Data Docker Artifact Detail
|
||||||
|
Data DockerArtifactDetail `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerArtifactManifestResponse defines model for DockerArtifactManifestResponse.
|
||||||
|
type DockerArtifactManifestResponse struct {
|
||||||
|
// Data Docker Artifact Manifest
|
||||||
|
Data DockerArtifactManifest `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerLayersResponse defines model for DockerLayersResponse.
|
||||||
|
type DockerLayersResponse struct {
|
||||||
|
// Data Harness Layers Summary
|
||||||
|
Data DockerLayersSummary `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerManifestsResponse defines model for DockerManifestsResponse.
|
||||||
|
type DockerManifestsResponse struct {
|
||||||
|
// Data Harness Manifests
|
||||||
|
Data DockerManifests `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelmArtifactDetailResponse defines model for HelmArtifactDetailResponse.
|
||||||
|
type HelmArtifactDetailResponse struct {
|
||||||
|
// Data Helm Artifact Detail
|
||||||
|
Data HelmArtifactDetail `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelmArtifactManifestResponse defines model for HelmArtifactManifestResponse.
|
||||||
|
type HelmArtifactManifestResponse struct {
|
||||||
|
// Data Helm Artifact Manifest
|
||||||
|
Data HelmArtifactManifest `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalServerError defines model for InternalServerError.
|
||||||
|
type InternalServerError Error
|
||||||
|
|
||||||
|
// ListArtifactLabelResponse defines model for ListArtifactLabelResponse.
|
||||||
|
type ListArtifactLabelResponse struct {
|
||||||
|
// Data A list of Harness Artifact Labels
|
||||||
|
Data ListArtifactLabel `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListArtifactResponse defines model for ListArtifactResponse.
|
||||||
|
type ListArtifactResponse struct {
|
||||||
|
// Data A list of Artifacts
|
||||||
|
Data ListArtifact `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListArtifactVersionResponse defines model for ListArtifactVersionResponse.
|
||||||
|
type ListArtifactVersionResponse struct {
|
||||||
|
// Data A list of Artifact versions
|
||||||
|
Data ListArtifactVersion `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRegistryResponse defines model for ListRegistryResponse.
|
||||||
|
type ListRegistryResponse struct {
|
||||||
|
// Data A list of Harness Artifact Registries
|
||||||
|
Data ListRegistry `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFound defines model for NotFound.
|
||||||
|
type NotFound Error
|
||||||
|
|
||||||
|
// RegistryResponse defines model for RegistryResponse.
|
||||||
|
type RegistryResponse struct {
|
||||||
|
// Data Harness Artifact Registry
|
||||||
|
Data Registry `json:"data"`
|
||||||
|
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success defines model for Success.
|
||||||
|
type Success struct {
|
||||||
|
// Status Indicates if the request was successful or not
|
||||||
|
Status Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unauthenticated defines model for Unauthenticated.
|
||||||
|
type Unauthenticated Error
|
||||||
|
|
||||||
|
// Unauthorized defines model for Unauthorized.
|
||||||
|
type Unauthorized Error
|
||||||
|
|
||||||
|
// ListArtifactLabelsParams defines parameters for ListArtifactLabels.
|
||||||
|
type ListArtifactLabelsParams struct {
|
||||||
|
// Page Current page number
|
||||||
|
Page *PageNumber `form:"page,omitempty" json:"page,omitempty"`
|
||||||
|
|
||||||
|
// Size Number of items per page
|
||||||
|
Size *PageSize `form:"size,omitempty" json:"size,omitempty"`
|
||||||
|
|
||||||
|
// SearchTerm search Term.
|
||||||
|
SearchTerm *SearchTerm `form:"search_term,omitempty" json:"search_term,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArtifactStatsForRegistryParams defines parameters for GetArtifactStatsForRegistry.
|
||||||
|
type GetArtifactStatsForRegistryParams struct {
|
||||||
|
// From Date. Format - MM/DD/YYYY
|
||||||
|
From *FromDateParam `form:"from,omitempty" json:"from,omitempty"`
|
||||||
|
|
||||||
|
// To Date. Format - MM/DD/YYYY
|
||||||
|
To *ToDateParam `form:"to,omitempty" json:"to,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArtifactStatsParams defines parameters for GetArtifactStats.
|
||||||
|
type GetArtifactStatsParams struct {
|
||||||
|
// From Date. Format - MM/DD/YYYY
|
||||||
|
From *FromDateParam `form:"from,omitempty" json:"from,omitempty"`
|
||||||
|
|
||||||
|
// To Date. Format - MM/DD/YYYY
|
||||||
|
To *ToDateParam `form:"to,omitempty" json:"to,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDockerArtifactDetailsParams defines parameters for GetDockerArtifactDetails.
|
||||||
|
type GetDockerArtifactDetailsParams struct {
|
||||||
|
// Digest Digest.
|
||||||
|
Digest DigestParam `form:"digest" json:"digest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDockerArtifactLayersParams defines parameters for GetDockerArtifactLayers.
|
||||||
|
type GetDockerArtifactLayersParams struct {
|
||||||
|
// Digest Digest.
|
||||||
|
Digest DigestParam `form:"digest" json:"digest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDockerArtifactManifestParams defines parameters for GetDockerArtifactManifest.
|
||||||
|
type GetDockerArtifactManifestParams struct {
|
||||||
|
// Digest Digest.
|
||||||
|
Digest DigestParam `form:"digest" json:"digest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllArtifactVersionsParams defines parameters for GetAllArtifactVersions.
|
||||||
|
type GetAllArtifactVersionsParams struct {
|
||||||
|
// Page Current page number
|
||||||
|
Page *PageNumber `form:"page,omitempty" json:"page,omitempty"`
|
||||||
|
|
||||||
|
// Size Number of items per page
|
||||||
|
Size *PageSize `form:"size,omitempty" json:"size,omitempty"`
|
||||||
|
|
||||||
|
// SortOrder sortOrder
|
||||||
|
SortOrder *SortOrder `form:"sort_order,omitempty" json:"sort_order,omitempty"`
|
||||||
|
|
||||||
|
// SortField sortField
|
||||||
|
SortField *SortField `form:"sort_field,omitempty" json:"sort_field,omitempty"`
|
||||||
|
|
||||||
|
// SearchTerm search Term.
|
||||||
|
SearchTerm *SearchTerm `form:"search_term,omitempty" json:"search_term,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientSetupDetailsParams defines parameters for GetClientSetupDetails.
|
||||||
|
type GetClientSetupDetailsParams struct {
|
||||||
|
// Artifact Artifat
|
||||||
|
Artifact *ArtifactParam `form:"artifact,omitempty" json:"artifact,omitempty"`
|
||||||
|
|
||||||
|
// Version Version
|
||||||
|
Version *VersionParam `form:"version,omitempty" json:"version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArtifactStatsForSpaceParams defines parameters for GetArtifactStatsForSpace.
|
||||||
|
type GetArtifactStatsForSpaceParams struct {
|
||||||
|
// From Date. Format - MM/DD/YYYY
|
||||||
|
From *FromDateParam `form:"from,omitempty" json:"from,omitempty"`
|
||||||
|
|
||||||
|
// To Date. Format - MM/DD/YYYY
|
||||||
|
To *ToDateParam `form:"to,omitempty" json:"to,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllArtifactsParams defines parameters for GetAllArtifacts.
|
||||||
|
type GetAllArtifactsParams struct {
|
||||||
|
// Label Label.
|
||||||
|
Label *LabelsParam `form:"label,omitempty" json:"label,omitempty"`
|
||||||
|
|
||||||
|
// PackageType Registry Package Type
|
||||||
|
PackageType *PackageTypeParam `form:"package_type,omitempty" json:"package_type,omitempty"`
|
||||||
|
|
||||||
|
// RegIdentifier Registry Identifier
|
||||||
|
RegIdentifier *RegistryIdentifierParam `form:"reg_identifier,omitempty" json:"reg_identifier,omitempty"`
|
||||||
|
|
||||||
|
// Page Current page number
|
||||||
|
Page *PageNumber `form:"page,omitempty" json:"page,omitempty"`
|
||||||
|
|
||||||
|
// Size Number of items per page
|
||||||
|
Size *PageSize `form:"size,omitempty" json:"size,omitempty"`
|
||||||
|
|
||||||
|
// SortOrder sortOrder
|
||||||
|
SortOrder *SortOrder `form:"sort_order,omitempty" json:"sort_order,omitempty"`
|
||||||
|
|
||||||
|
// SortField sortField
|
||||||
|
SortField *SortField `form:"sort_field,omitempty" json:"sort_field,omitempty"`
|
||||||
|
|
||||||
|
// SearchTerm search Term.
|
||||||
|
SearchTerm *SearchTerm `form:"search_term,omitempty" json:"search_term,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllRegistriesParams defines parameters for GetAllRegistries.
|
||||||
|
type GetAllRegistriesParams struct {
|
||||||
|
// PackageType Registry Package Type
|
||||||
|
PackageType *PackageTypeParam `form:"package_type,omitempty" json:"package_type,omitempty"`
|
||||||
|
|
||||||
|
// Type Registry Type
|
||||||
|
Type *GetAllRegistriesParamsType `form:"type,omitempty" json:"type,omitempty"`
|
||||||
|
|
||||||
|
// Page Current page number
|
||||||
|
Page *PageNumber `form:"page,omitempty" json:"page,omitempty"`
|
||||||
|
|
||||||
|
// Size Number of items per page
|
||||||
|
Size *PageSize `form:"size,omitempty" json:"size,omitempty"`
|
||||||
|
|
||||||
|
// SortOrder sortOrder
|
||||||
|
SortOrder *SortOrder `form:"sort_order,omitempty" json:"sort_order,omitempty"`
|
||||||
|
|
||||||
|
// SortField sortField
|
||||||
|
SortField *SortField `form:"sort_field,omitempty" json:"sort_field,omitempty"`
|
||||||
|
|
||||||
|
// SearchTerm search Term.
|
||||||
|
SearchTerm *SearchTerm `form:"search_term,omitempty" json:"search_term,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllRegistriesParamsType defines parameters for GetAllRegistries.
|
||||||
|
type GetAllRegistriesParamsType string
|
||||||
|
|
||||||
|
// CreateRegistryJSONRequestBody defines body for CreateRegistry for application/json ContentType.
|
||||||
|
type CreateRegistryJSONRequestBody RegistryRequest
|
||||||
|
|
||||||
|
// ModifyRegistryJSONRequestBody defines body for ModifyRegistry for application/json ContentType.
|
||||||
|
type ModifyRegistryJSONRequestBody RegistryRequest
|
||||||
|
|
||||||
|
// UpdateArtifactLabelsJSONRequestBody defines body for UpdateArtifactLabels for application/json ContentType.
|
||||||
|
type UpdateArtifactLabelsJSONRequestBody ArtifactLabelRequest
|
||||||
|
|
||||||
|
// AsVirtualConfig returns the union data inside the RegistryConfig as a VirtualConfig
|
||||||
|
func (t RegistryConfig) AsVirtualConfig() (VirtualConfig, error) {
|
||||||
|
var body VirtualConfig
|
||||||
|
err := json.Unmarshal(t.union, &body)
|
||||||
|
return body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromVirtualConfig overwrites any union data inside the RegistryConfig as the provided VirtualConfig
|
||||||
|
func (t *RegistryConfig) FromVirtualConfig(v VirtualConfig) error {
|
||||||
|
t.Type = "VIRTUAL"
|
||||||
|
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
t.union = b
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeVirtualConfig performs a merge with any union data inside the RegistryConfig, using the provided VirtualConfig
|
||||||
|
func (t *RegistryConfig) MergeVirtualConfig(v VirtualConfig) error {
|
||||||
|
t.Type = "VIRTUAL"
|
||||||
|
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
merged, err := runtime.JSONMerge(t.union, b)
|
||||||
|
t.union = merged
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsUpstreamConfig returns the union data inside the RegistryConfig as a UpstreamConfig
|
||||||
|
func (t RegistryConfig) AsUpstreamConfig() (UpstreamConfig, error) {
|
||||||
|
var body UpstreamConfig
|
||||||
|
err := json.Unmarshal(t.union, &body)
|
||||||
|
return body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromUpstreamConfig overwrites any union data inside the RegistryConfig as the provided UpstreamConfig
|
||||||
|
func (t *RegistryConfig) FromUpstreamConfig(v UpstreamConfig) error {
|
||||||
|
t.Type = "UPSTREAM"
|
||||||
|
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
t.union = b
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeUpstreamConfig performs a merge with any union data inside the RegistryConfig, using the provided UpstreamConfig
|
||||||
|
func (t *RegistryConfig) MergeUpstreamConfig(v UpstreamConfig) error {
|
||||||
|
t.Type = "UPSTREAM"
|
||||||
|
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
merged, err := runtime.JSONMerge(t.union, b)
|
||||||
|
t.union = merged
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t RegistryConfig) Discriminator() (string, error) {
|
||||||
|
var discriminator struct {
|
||||||
|
Discriminator string `json:"type"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(t.union, &discriminator)
|
||||||
|
return discriminator.Discriminator, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t RegistryConfig) ValueByDiscriminator() (interface{}, error) {
|
||||||
|
discriminator, err := t.Discriminator()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch discriminator {
|
||||||
|
case "UPSTREAM":
|
||||||
|
return t.AsUpstreamConfig()
|
||||||
|
case "VIRTUAL":
|
||||||
|
return t.AsVirtualConfig()
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown discriminator value: " + discriminator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t RegistryConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
b, err := t.union.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
object := make(map[string]json.RawMessage)
|
||||||
|
if t.union != nil {
|
||||||
|
err = json.Unmarshal(b, &object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object["type"], err = json.Marshal(t.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error marshaling 'type': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err = json.Marshal(object)
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *RegistryConfig) UnmarshalJSON(b []byte) error {
|
||||||
|
err := t.union.UnmarshalJSON(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
object := make(map[string]json.RawMessage)
|
||||||
|
err = json.Unmarshal(b, &object)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw, found := object["type"]; found {
|
||||||
|
err = json.Unmarshal(raw, &t.Type)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading 'type': %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsUserPassword returns the union data inside the UpstreamConfig_Auth as a UserPassword
|
||||||
|
func (t UpstreamConfig_Auth) AsUserPassword() (UserPassword, error) {
|
||||||
|
var body UserPassword
|
||||||
|
err := json.Unmarshal(t.union, &body)
|
||||||
|
return body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromUserPassword overwrites any union data inside the UpstreamConfig_Auth as the provided UserPassword
|
||||||
|
func (t *UpstreamConfig_Auth) FromUserPassword(v UserPassword) error {
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
t.union = b
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeUserPassword performs a merge with any union data inside the UpstreamConfig_Auth, using the provided UserPassword
|
||||||
|
func (t *UpstreamConfig_Auth) MergeUserPassword(v UserPassword) error {
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
merged, err := runtime.JSONMerge(t.union, b)
|
||||||
|
t.union = merged
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsAnonymous returns the union data inside the UpstreamConfig_Auth as a Anonymous
|
||||||
|
func (t UpstreamConfig_Auth) AsAnonymous() (Anonymous, error) {
|
||||||
|
var body Anonymous
|
||||||
|
err := json.Unmarshal(t.union, &body)
|
||||||
|
return body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromAnonymous overwrites any union data inside the UpstreamConfig_Auth as the provided Anonymous
|
||||||
|
func (t *UpstreamConfig_Auth) FromAnonymous(v Anonymous) error {
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
t.union = b
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeAnonymous performs a merge with any union data inside the UpstreamConfig_Auth, using the provided Anonymous
|
||||||
|
func (t *UpstreamConfig_Auth) MergeAnonymous(v Anonymous) error {
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
merged, err := runtime.JSONMerge(t.union, b)
|
||||||
|
t.union = merged
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t UpstreamConfig_Auth) MarshalJSON() ([]byte, error) {
|
||||||
|
b, err := t.union.MarshalJSON()
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UpstreamConfig_Auth) UnmarshalJSON(b []byte) error {
|
||||||
|
err := t.union.UnmarshalJSON(b)
|
||||||
|
return err
|
||||||
|
}
|
76
registry/app/api/router/harness/route.go
Normal file
76
registry/app/api/router/harness/route.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package harness
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
middlewareauthn "github.com/harness/gitness/app/api/middleware/authn"
|
||||||
|
"github.com/harness/gitness/app/auth/authn"
|
||||||
|
"github.com/harness/gitness/app/auth/authz"
|
||||||
|
corestore "github.com/harness/gitness/app/store"
|
||||||
|
urlprovider "github.com/harness/gitness/app/url"
|
||||||
|
"github.com/harness/gitness/audit"
|
||||||
|
"github.com/harness/gitness/registry/app/api/controller/metadata"
|
||||||
|
"github.com/harness/gitness/registry/app/api/middleware"
|
||||||
|
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||||
|
storagedriver "github.com/harness/gitness/registry/app/driver"
|
||||||
|
"github.com/harness/gitness/registry/app/store"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIHandler interface {
|
||||||
|
http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPIHandler(
|
||||||
|
repoDao store.RegistryRepository,
|
||||||
|
upstreamproxyDao store.UpstreamProxyConfigRepository,
|
||||||
|
tagDao store.TagRepository,
|
||||||
|
manifestDao store.ManifestRepository,
|
||||||
|
cleanupPolicyDao store.CleanupPolicyRepository,
|
||||||
|
artifactDao store.ArtifactRepository,
|
||||||
|
driver storagedriver.StorageDriver,
|
||||||
|
baseURL string,
|
||||||
|
spaceStore corestore.SpaceStore,
|
||||||
|
tx dbtx.Transactor,
|
||||||
|
authenticator authn.Authenticator,
|
||||||
|
urlProvider urlprovider.Provider,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
auditService audit.Service,
|
||||||
|
) APIHandler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(audit.Middleware())
|
||||||
|
r.Use(middlewareauthn.Attempt(authenticator))
|
||||||
|
r.Use(middleware.CheckAuth())
|
||||||
|
apiController := metadata.NewAPIController(
|
||||||
|
repoDao,
|
||||||
|
upstreamproxyDao,
|
||||||
|
tagDao,
|
||||||
|
manifestDao,
|
||||||
|
cleanupPolicyDao,
|
||||||
|
artifactDao,
|
||||||
|
driver,
|
||||||
|
spaceStore,
|
||||||
|
tx,
|
||||||
|
urlProvider,
|
||||||
|
authorizer,
|
||||||
|
auditService,
|
||||||
|
)
|
||||||
|
handler := artifact.NewStrictHandler(apiController, []artifact.StrictMiddlewareFunc{})
|
||||||
|
return artifact.HandlerFromMuxWithBaseURL(handler, r, baseURL)
|
||||||
|
}
|
151
registry/app/api/router/oci/route.go
Normal file
151
registry/app/api/router/oci/route.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
middlewareauthn "github.com/harness/gitness/app/api/middleware/authn"
|
||||||
|
"github.com/harness/gitness/registry/app/api/handler/oci"
|
||||||
|
"github.com/harness/gitness/registry/app/api/middleware"
|
||||||
|
"github.com/harness/gitness/registry/app/common"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RouteType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Manifests RouteType = "manifests" // /v2/:registry/:image/manifests/:reference.
|
||||||
|
Blobs RouteType = "blobs" // /v2/:registry/:image/blobs/:digest.
|
||||||
|
BlobsUploadsSession RouteType = "blob-uploads-session" // /v2/:registry/:image/blobs/uploads/:session_id.
|
||||||
|
Tags RouteType = "tags" // /v2/:registry/:image/tags/list.
|
||||||
|
Referrers RouteType = "referrers" // /v2/:registry/:image/referrers/:digest.
|
||||||
|
Invalid RouteType = "invalid" // Invalid route.
|
||||||
|
// Add other route types here.
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRouteTypeV2(url string) RouteType {
|
||||||
|
url = strings.Trim(url, "/")
|
||||||
|
segments := strings.Split(url, "/")
|
||||||
|
if len(segments) < 4 {
|
||||||
|
return Invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := segments[len(segments)-2]
|
||||||
|
|
||||||
|
switch typ {
|
||||||
|
case "manifests":
|
||||||
|
return Manifests
|
||||||
|
case "blobs":
|
||||||
|
if segments[len(segments)-1] == "uploads" {
|
||||||
|
return BlobsUploadsSession
|
||||||
|
}
|
||||||
|
return Blobs
|
||||||
|
case "uploads":
|
||||||
|
return BlobsUploadsSession
|
||||||
|
case "tags":
|
||||||
|
return Tags
|
||||||
|
case "referrers":
|
||||||
|
return Referrers
|
||||||
|
}
|
||||||
|
return Invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandlerBlock struct {
|
||||||
|
Handler2 http.HandlerFunc
|
||||||
|
RemoteSupport bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlerBlock2(h2 http.HandlerFunc, remoteSupport bool) HandlerBlock {
|
||||||
|
return HandlerBlock{
|
||||||
|
Handler2: h2,
|
||||||
|
RemoteSupport: remoteSupport,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegistryOCIHandler interface {
|
||||||
|
http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOCIHandler(handlerV2 *oci.Handler) RegistryOCIHandler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
var routeHandlers = map[RouteType]map[string]HandlerBlock{
|
||||||
|
Manifests: {
|
||||||
|
http.MethodGet: NewHandlerBlock2(handlerV2.GetManifest, true),
|
||||||
|
http.MethodHead: NewHandlerBlock2(handlerV2.HeadManifest, true),
|
||||||
|
http.MethodPut: NewHandlerBlock2(handlerV2.PutManifest, false),
|
||||||
|
http.MethodDelete: NewHandlerBlock2(handlerV2.DeleteManifest, false),
|
||||||
|
},
|
||||||
|
Blobs: {
|
||||||
|
http.MethodGet: NewHandlerBlock2(handlerV2.GetBlob, true),
|
||||||
|
http.MethodHead: NewHandlerBlock2(handlerV2.HeadBlob, false),
|
||||||
|
http.MethodDelete: NewHandlerBlock2(handlerV2.DeleteBlob, false),
|
||||||
|
},
|
||||||
|
BlobsUploadsSession: {
|
||||||
|
http.MethodGet: NewHandlerBlock2(handlerV2.GetUploadBlobStatus, false),
|
||||||
|
http.MethodPatch: NewHandlerBlock2(handlerV2.PatchBlobUpload, false),
|
||||||
|
http.MethodPut: NewHandlerBlock2(handlerV2.CompleteBlobUpload, false),
|
||||||
|
http.MethodDelete: NewHandlerBlock2(handlerV2.CancelBlobUpload, false),
|
||||||
|
http.MethodPost: NewHandlerBlock2(handlerV2.InitiateUploadBlob, false),
|
||||||
|
},
|
||||||
|
Tags: {
|
||||||
|
http.MethodGet: NewHandlerBlock2(handlerV2.GetTags, false),
|
||||||
|
},
|
||||||
|
Referrers: {
|
||||||
|
http.MethodGet: NewHandlerBlock2(handlerV2.GetReferrers, false),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r.Route("/v2", func(r chi.Router) {
|
||||||
|
r.Use(middlewareauthn.Attempt(handlerV2.Authenticator))
|
||||||
|
r.Get("/token", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
handlerV2.GetToken(w, req)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.With(middleware.OciCheckAuth(common.GenerateOciTokenURL(handlerV2.URLProvider.RegistryURL()))).
|
||||||
|
Get("/", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
handlerV2.APIBase(w, req)
|
||||||
|
})
|
||||||
|
r.Route("/{registryIdentifier}", func(r chi.Router) {
|
||||||
|
r.Use(middleware.OciCheckAuth(common.GenerateOciTokenURL(handlerV2.URLProvider.RegistryURL())))
|
||||||
|
r.Use(middleware.BlockNonOciSourceToken(handlerV2.URLProvider.RegistryURL()))
|
||||||
|
r.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
path := req.URL.Path
|
||||||
|
methodType := req.Method
|
||||||
|
|
||||||
|
requestType := GetRouteTypeV2(path)
|
||||||
|
|
||||||
|
if _, ok := routeHandlers[requestType]; ok {
|
||||||
|
if h, ok2 := routeHandlers[requestType][methodType]; ok2 {
|
||||||
|
h.Handler2(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
_, err := w.Write([]byte("Invalid route"))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to write response")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
51
registry/app/api/router/registry_router.go
Normal file
51
registry/app/api/router/registry_router.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RegistryMount = "/api/v1/registry"
|
||||||
|
const APIMount = "/api"
|
||||||
|
|
||||||
|
type RegistryRouter struct {
|
||||||
|
handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRegistryRouter(handler http.Handler) *RegistryRouter {
|
||||||
|
return &RegistryRouter{handler: handler}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RegistryRouter) Handle(w http.ResponseWriter, req *http.Request) {
|
||||||
|
r.handler.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RegistryRouter) IsEligibleTraffic(req *http.Request) bool {
|
||||||
|
if strings.HasPrefix(req.URL.Path, RegistryMount) || strings.HasPrefix(req.URL.Path, "/v2/") ||
|
||||||
|
strings.HasPrefix(req.URL.Path, "/registry/") ||
|
||||||
|
(strings.HasPrefix(req.URL.Path, APIMount+"/v1/spaces/") &&
|
||||||
|
(strings.HasSuffix(req.URL.Path, "/artifacts") ||
|
||||||
|
strings.HasSuffix(req.URL.Path, "/registries"))) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RegistryRouter) Name() string {
|
||||||
|
return "registry"
|
||||||
|
}
|
54
registry/app/api/router/router.go
Normal file
54
registry/app/api/router/router.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/app/api/middleware/address"
|
||||||
|
"github.com/harness/gitness/app/api/middleware/logging"
|
||||||
|
"github.com/harness/gitness/registry/app/api/handler/swagger"
|
||||||
|
"github.com/harness/gitness/registry/app/api/router/harness"
|
||||||
|
"github.com/harness/gitness/registry/app/api/router/oci"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/rs/zerolog/hlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppRouter interface {
|
||||||
|
http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAppRouter(
|
||||||
|
ociHandler oci.RegistryOCIHandler,
|
||||||
|
appHandler harness.APIHandler,
|
||||||
|
baseURL string,
|
||||||
|
) AppRouter {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(hlog.URLHandler("http.url"))
|
||||||
|
r.Use(hlog.MethodHandler("http.method"))
|
||||||
|
r.Use(logging.HLogRequestIDHandler())
|
||||||
|
r.Use(logging.HLogAccessLogHandler())
|
||||||
|
r.Use(address.Handler("", ""))
|
||||||
|
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Handle(fmt.Sprintf("%s/*", baseURL), appHandler)
|
||||||
|
r.Handle("/v2/*", ociHandler)
|
||||||
|
|
||||||
|
r.Handle("/registry/swagger*", swagger.GetSwaggerHandler("/registry"))
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
}
|
78
registry/app/api/router/wire.go
Normal file
78
registry/app/api/router/wire.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/app/auth/authn"
|
||||||
|
"github.com/harness/gitness/app/auth/authz"
|
||||||
|
"github.com/harness/gitness/app/config"
|
||||||
|
corestore "github.com/harness/gitness/app/store"
|
||||||
|
urlprovider "github.com/harness/gitness/app/url"
|
||||||
|
"github.com/harness/gitness/audit"
|
||||||
|
hoci "github.com/harness/gitness/registry/app/api/handler/oci"
|
||||||
|
"github.com/harness/gitness/registry/app/api/router/harness"
|
||||||
|
"github.com/harness/gitness/registry/app/api/router/oci"
|
||||||
|
storagedriver "github.com/harness/gitness/registry/app/driver"
|
||||||
|
"github.com/harness/gitness/registry/app/store"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AppRouterProvider(
|
||||||
|
ocir oci.RegistryOCIHandler,
|
||||||
|
appHandler harness.APIHandler,
|
||||||
|
) AppRouter {
|
||||||
|
return GetAppRouter(ocir, appHandler, config.APIURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func APIHandlerProvider(
|
||||||
|
repoDao store.RegistryRepository,
|
||||||
|
upstreamproxyDao store.UpstreamProxyConfigRepository,
|
||||||
|
tagDao store.TagRepository,
|
||||||
|
manifestDao store.ManifestRepository,
|
||||||
|
cleanupPolicyDao store.CleanupPolicyRepository,
|
||||||
|
artifactDao store.ArtifactRepository,
|
||||||
|
driver storagedriver.StorageDriver,
|
||||||
|
spaceStore corestore.SpaceStore,
|
||||||
|
tx dbtx.Transactor,
|
||||||
|
authenticator authn.Authenticator,
|
||||||
|
urlProvider urlprovider.Provider,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
auditService audit.Service,
|
||||||
|
) harness.APIHandler {
|
||||||
|
return harness.NewAPIHandler(
|
||||||
|
repoDao,
|
||||||
|
upstreamproxyDao,
|
||||||
|
tagDao,
|
||||||
|
manifestDao,
|
||||||
|
cleanupPolicyDao,
|
||||||
|
artifactDao,
|
||||||
|
driver,
|
||||||
|
config.APIURL,
|
||||||
|
spaceStore,
|
||||||
|
tx,
|
||||||
|
authenticator,
|
||||||
|
urlProvider,
|
||||||
|
authorizer,
|
||||||
|
auditService,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OCIHandlerProvider(handlerV2 *hoci.Handler) oci.RegistryOCIHandler {
|
||||||
|
return oci.NewOCIHandler(handlerV2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var WireSet = wire.NewSet(APIHandlerProvider, OCIHandlerProvider, AppRouterProvider)
|
85
registry/app/api/wire.go
Normal file
85
registry/app/api/wire.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
usercontroller "github.com/harness/gitness/app/api/controller/user"
|
||||||
|
"github.com/harness/gitness/app/auth/authn"
|
||||||
|
"github.com/harness/gitness/app/auth/authz"
|
||||||
|
corestore "github.com/harness/gitness/app/store"
|
||||||
|
urlprovider "github.com/harness/gitness/app/url"
|
||||||
|
ocihandler "github.com/harness/gitness/registry/app/api/handler/oci"
|
||||||
|
"github.com/harness/gitness/registry/app/api/router"
|
||||||
|
storagedriver "github.com/harness/gitness/registry/app/driver"
|
||||||
|
"github.com/harness/gitness/registry/app/driver/factory"
|
||||||
|
"github.com/harness/gitness/registry/app/driver/filesystem"
|
||||||
|
"github.com/harness/gitness/registry/app/driver/s3-aws"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg"
|
||||||
|
"github.com/harness/gitness/registry/app/pkg/docker"
|
||||||
|
"github.com/harness/gitness/registry/app/store/database"
|
||||||
|
"github.com/harness/gitness/registry/config"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RegistryApp struct {
|
||||||
|
Config *types.Config
|
||||||
|
|
||||||
|
AppRouter router.AppRouter
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlobStorageProvider(c *types.Config) (storagedriver.StorageDriver, error) {
|
||||||
|
var d storagedriver.StorageDriver
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if c.Registry.Storage.StorageType == "filesystem" {
|
||||||
|
filesystem.Register()
|
||||||
|
d, err = factory.Create("filesystem", config.GetFilesystemParams(c))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Stack().Err(err).Msgf("")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s3.Register()
|
||||||
|
d, err = factory.Create("s3aws", config.GetS3StorageParameters(c))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Stack().Err(err).Msg("failed to init s3 Blob storage ")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlerProvider(controller *docker.Controller, spaceStore corestore.SpaceStore,
|
||||||
|
tokenStore corestore.TokenStore, userCtrl *usercontroller.Controller, authenticator authn.Authenticator,
|
||||||
|
urlProvider urlprovider.Provider, authorizer authz.Authorizer) *ocihandler.Handler {
|
||||||
|
return ocihandler.NewHandler(controller, spaceStore, tokenStore, userCtrl, authenticator, urlProvider, authorizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var WireSet = wire.NewSet(
|
||||||
|
BlobStorageProvider,
|
||||||
|
NewHandlerProvider,
|
||||||
|
database.WireSet,
|
||||||
|
pkg.WireSet,
|
||||||
|
docker.WireSet,
|
||||||
|
router.WireSet,
|
||||||
|
)
|
||||||
|
|
||||||
|
func Wire(_ *types.Config) (RegistryApp, error) {
|
||||||
|
wire.Build(WireSet, wire.Struct(new(RegistryApp), "*"))
|
||||||
|
return RegistryApp{}, nil
|
||||||
|
}
|
169
registry/app/auth/auth.go
Normal file
169
registry/app/auth/auth.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessSet maps a typed, named resource to
|
||||||
|
// a set of actions requested or authorized.
|
||||||
|
type AccessSet map[Resource]ActionSet
|
||||||
|
|
||||||
|
// NewAccessSet constructs an accessSet from
|
||||||
|
// a variable number of auth.Access items.
|
||||||
|
func NewAccessSet(accessItems ...Access) AccessSet {
|
||||||
|
accessSet := make(AccessSet, len(accessItems))
|
||||||
|
|
||||||
|
for _, access := range accessItems {
|
||||||
|
resource := Resource{
|
||||||
|
Type: access.Type,
|
||||||
|
Name: access.Name,
|
||||||
|
Space: access.Space,
|
||||||
|
}
|
||||||
|
|
||||||
|
set, exists := accessSet[resource]
|
||||||
|
if !exists {
|
||||||
|
set = NewActionSet()
|
||||||
|
accessSet[resource] = set
|
||||||
|
}
|
||||||
|
|
||||||
|
set.Add(access.Action)
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns whether or not the given access is in this accessSet.
|
||||||
|
func (s AccessSet) Contains(access Access) bool {
|
||||||
|
actionSet, ok := s[access.Resource]
|
||||||
|
if ok {
|
||||||
|
return actionSet.contains(access.Action)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScopeParam returns a collection of scopes which can
|
||||||
|
// be used for a WWW-Authenticate challenge parameter.
|
||||||
|
func (s AccessSet) ScopeParam() string {
|
||||||
|
scopes := make([]string, 0, len(s))
|
||||||
|
|
||||||
|
for resource, actionSet := range s {
|
||||||
|
actions := strings.Join(actionSet.keys(), ",")
|
||||||
|
resourceName := strings.Join([]string{resource.Space, resource.Name}, "/")
|
||||||
|
scopes = append(scopes, strings.Join([]string{resource.Type, resourceName, actions}, ":"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(scopes, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource describes a resource by type and name.
|
||||||
|
type Resource struct {
|
||||||
|
Type string
|
||||||
|
Name string
|
||||||
|
Space string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access describes a specific action that is
|
||||||
|
// requested or allowed for a given resource.
|
||||||
|
type Access struct {
|
||||||
|
Resource
|
||||||
|
Action string
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppendAccess(records []Access, method string, rootIdentifier string, repo string) []Access {
|
||||||
|
resource := Resource{
|
||||||
|
Type: "repository",
|
||||||
|
Name: repo,
|
||||||
|
Space: rootIdentifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case http.MethodGet, http.MethodHead:
|
||||||
|
records = append(records,
|
||||||
|
Access{
|
||||||
|
Resource: resource,
|
||||||
|
Action: "pull",
|
||||||
|
})
|
||||||
|
case http.MethodPost, http.MethodPut, http.MethodPatch:
|
||||||
|
records = append(records,
|
||||||
|
Access{
|
||||||
|
Resource: resource,
|
||||||
|
Action: "pull",
|
||||||
|
},
|
||||||
|
Access{
|
||||||
|
Resource: resource,
|
||||||
|
Action: "push",
|
||||||
|
})
|
||||||
|
case http.MethodDelete:
|
||||||
|
records = append(records,
|
||||||
|
Access{
|
||||||
|
Resource: resource,
|
||||||
|
Action: "delete",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionSet is a special type of stringSet.
|
||||||
|
type ActionSet struct {
|
||||||
|
stringSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewActionSet(actions ...string) ActionSet {
|
||||||
|
return ActionSet{newStringSet(actions...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains calls StringSet.Contains() for
|
||||||
|
// either "*" or the given action string.
|
||||||
|
func (s ActionSet) Contains(action string) bool {
|
||||||
|
return s.stringSet.contains("*") || s.stringSet.contains(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSet is a useful type for looking up strings.
|
||||||
|
type stringSet map[string]struct{}
|
||||||
|
|
||||||
|
// NewStringSet creates a new StringSet with the given strings.
|
||||||
|
func newStringSet(keys ...string) stringSet {
|
||||||
|
ss := make(stringSet, len(keys))
|
||||||
|
ss.Add(keys...)
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add inserts the given keys into this StringSet.
|
||||||
|
func (ss stringSet) Add(keys ...string) {
|
||||||
|
for _, key := range keys {
|
||||||
|
ss[key] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns whether the given key is in this StringSet.
|
||||||
|
func (ss stringSet) contains(key string) bool {
|
||||||
|
_, ok := ss[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of all keys in this StringSet.
|
||||||
|
func (ss stringSet) keys() []string {
|
||||||
|
keys := make([]string, 0, len(ss))
|
||||||
|
|
||||||
|
for key := range ss {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
26
registry/app/common/http/modifier/modifier.go
Normal file
26
registry/app/common/http/modifier/modifier.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package modifier
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Modifier modifies request.
|
||||||
|
type Modifier interface {
|
||||||
|
Modify(*http.Request) error
|
||||||
|
}
|
84
registry/app/common/http/tls.go
Normal file
84
registry/app/common/http/tls.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Internal TLS ENV.
|
||||||
|
internalTLSEnable = "GITNESS_INTERNAL_TLS_ENABLED"
|
||||||
|
internalVerifyClientCert = "GITNESS_INTERNAL_VERIFY_CLIENT_CERT"
|
||||||
|
internalTLSKeyPath = "GITNESS_INTERNAL_TLS_KEY_PATH"
|
||||||
|
internalTLSCertPath = "GITNESS_INTERNAL_TLS_CERT_PATH"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InternalTLSEnabled returns true if internal TLS enabled.
|
||||||
|
func InternalTLSEnabled() bool {
|
||||||
|
return strings.ToLower(os.Getenv(internalTLSEnable)) == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalEnableVerifyClientCert returns true if mTLS enabled.
|
||||||
|
func InternalEnableVerifyClientCert() bool {
|
||||||
|
return strings.ToLower(os.Getenv(internalVerifyClientCert)) == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInternalCertPair used to get internal cert and key pair from environment.
|
||||||
|
func GetInternalCertPair() (tls.Certificate, error) {
|
||||||
|
crtPath := os.Getenv(internalTLSCertPath)
|
||||||
|
keyPath := os.Getenv(internalTLSKeyPath)
|
||||||
|
return tls.LoadX509KeyPair(crtPath, keyPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInternalTLSConfig return a tls.Config for internal https communicate.
|
||||||
|
func GetInternalTLSConfig() (*tls.Config, error) {
|
||||||
|
// genrate key pair
|
||||||
|
cert, err := GetInternalCertPair()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("internal TLS enabled but can't get cert file %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerTLSConfig returns a modern tls config,
|
||||||
|
// refer to https://blog.cloudflare.com/exposing-go-on-the-internet/
|
||||||
|
func NewServerTLSConfig() *tls.Config {
|
||||||
|
return &tls.Config{
|
||||||
|
PreferServerCipherSuites: true,
|
||||||
|
CurvePreferences: []tls.CurveID{
|
||||||
|
tls.CurveP256,
|
||||||
|
tls.X25519,
|
||||||
|
},
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
CipherSuites: []uint16{
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
137
registry/app/common/http/transport.go
Normal file
137
registry/app/common/http/transport.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// InsecureTransport used to get the insecure http Transport.
|
||||||
|
InsecureTransport = iota
|
||||||
|
// SecureTransport used to get the external secure http Transport.
|
||||||
|
SecureTransport
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
secureHTTPTransport http.RoundTripper
|
||||||
|
insecureHTTPTransport http.RoundTripper
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
insecureHTTPTransport = NewTransport(WithInsecureSkipVerify(true))
|
||||||
|
if InternalTLSEnabled() {
|
||||||
|
secureHTTPTransport = NewTransport(WithInternalTLSConfig())
|
||||||
|
} else {
|
||||||
|
secureHTTPTransport = NewTransport()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this instead of Default Transport in library because it sets ForceAttemptHTTP2 to true
|
||||||
|
// And that options introduced in go 1.13 will cause the https requests hang forever in replication environment.
|
||||||
|
func newDefaultTransport() *http.Transport {
|
||||||
|
return &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
DualStack: true,
|
||||||
|
}).DialContext,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
},
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInternalTLSConfig returns a TransportOption that configures the transport to use the internal TLS configuration.
|
||||||
|
func WithInternalTLSConfig() func(*http.Transport) {
|
||||||
|
return func(tr *http.Transport) {
|
||||||
|
tlsConfig, err := GetInternalTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
tr.TLSClientConfig = tlsConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInsecureSkipVerify returns a TransportOption that configures the
|
||||||
|
// transport to skip verification of the server's certificate.
|
||||||
|
func WithInsecureSkipVerify(skipVerify bool) func(*http.Transport) {
|
||||||
|
return func(tr *http.Transport) {
|
||||||
|
tr.TLSClientConfig.InsecureSkipVerify = skipVerify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMaxIdleConns returns a TransportOption that configures the
|
||||||
|
// transport to use the specified number of idle connections per host.
|
||||||
|
func WithMaxIdleConns(maxIdleConns int) func(*http.Transport) {
|
||||||
|
return func(tr *http.Transport) {
|
||||||
|
tr.MaxIdleConns = maxIdleConns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithIdleconnectionTimeout returns a TransportOption that configures
|
||||||
|
// the transport to use the specified idle connection timeout.
|
||||||
|
func WithIdleconnectionTimeout(idleConnectionTimeout time.Duration) func(*http.Transport) {
|
||||||
|
return func(tr *http.Transport) {
|
||||||
|
tr.IdleConnTimeout = idleConnectionTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTransport returns a new http.Transport with the specified options.
|
||||||
|
func NewTransport(opts ...func(*http.Transport)) http.RoundTripper {
|
||||||
|
tr := newDefaultTransport()
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(tr)
|
||||||
|
}
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransportConfig is the configuration for http transport.
|
||||||
|
type TransportConfig struct {
|
||||||
|
Insecure bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransportOption is the option for http transport.
|
||||||
|
type TransportOption func(*TransportConfig)
|
||||||
|
|
||||||
|
// WithInsecure returns a TransportOption that configures the
|
||||||
|
// transport to skip verification of the server's certificate.
|
||||||
|
func WithInsecure(skipVerify bool) TransportOption {
|
||||||
|
return func(cfg *TransportConfig) {
|
||||||
|
cfg.Insecure = skipVerify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHTTPTransport returns HttpTransport based on insecure configuration.
|
||||||
|
func GetHTTPTransport(opts ...TransportOption) http.RoundTripper {
|
||||||
|
cfg := &TransportConfig{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cfg)
|
||||||
|
}
|
||||||
|
if cfg.Insecure {
|
||||||
|
return insecureHTTPTransport
|
||||||
|
}
|
||||||
|
return secureHTTPTransport
|
||||||
|
}
|
30
registry/app/common/http/transport_test.go
Normal file
30
registry/app/common/http/transport_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetHTTPTransport(t *testing.T) {
|
||||||
|
transport := GetHTTPTransport()
|
||||||
|
assert.Equal(t, secureHTTPTransport, transport, "Transport should be secure")
|
||||||
|
transport = GetHTTPTransport(WithInsecure(true))
|
||||||
|
assert.Equal(t, insecureHTTPTransport, transport, "Transport should be insecure")
|
||||||
|
}
|
24
registry/app/common/lib/authorizer.go
Normal file
24
registry/app/common/lib/authorizer.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/registry/app/common/http/modifier"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Authorizer authorizes the request.
|
||||||
|
type Authorizer modifier.Modifier
|
71
registry/app/common/lib/errors/const.go
Normal file
71
registry/app/common/lib/errors/const.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package errors
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NotFoundCode is code for the error of no object found.
|
||||||
|
NotFoundCode = "NOT_FOUND"
|
||||||
|
// ConflictCode ...
|
||||||
|
ConflictCode = "CONFLICT"
|
||||||
|
// UnAuthorizedCode ...
|
||||||
|
UnAuthorizedCode = "UNAUTHORIZED"
|
||||||
|
// BadRequestCode ...
|
||||||
|
BadRequestCode = "BAD_REQUEST"
|
||||||
|
// ForbiddenCode ...
|
||||||
|
ForbiddenCode = "FORBIDDEN"
|
||||||
|
// MethodNotAllowedCode ...
|
||||||
|
MethodNotAllowedCode = "METHOD_NOT_ALLOWED"
|
||||||
|
// RateLimitCode.
|
||||||
|
RateLimitCode = "TOO_MANY_REQUEST"
|
||||||
|
// PreconditionCode ...
|
||||||
|
PreconditionCode = "PRECONDITION"
|
||||||
|
// GeneralCode ...
|
||||||
|
GeneralCode = "UNKNOWN"
|
||||||
|
// DENIED it's used by middleware(readonly, vul and content trust)
|
||||||
|
// and returned to docker client to index the request is denied.
|
||||||
|
DENIED = "DENIED"
|
||||||
|
// PROJECTPOLICYVIOLATION ...
|
||||||
|
PROJECTPOLICYVIOLATION = "PROJECTPOLICYVIOLATION"
|
||||||
|
// ViolateForeignKeyConstraintCode is the error code for violating foreign key constraint error.
|
||||||
|
ViolateForeignKeyConstraintCode = "VIOLATE_FOREIGN_KEY_CONSTRAINT"
|
||||||
|
// DIGESTINVALID ...
|
||||||
|
DIGESTINVALID = "DIGEST_INVALID"
|
||||||
|
// MANIFESTINVALID ...
|
||||||
|
MANIFESTINVALID = "MANIFEST_INVALID"
|
||||||
|
// UNSUPPORTED is for digest UNSUPPORTED error.
|
||||||
|
UNSUPPORTED = "UNSUPPORTED"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotFoundError is error for the case of object not found.
|
||||||
|
func NotFoundError(err error) *Error {
|
||||||
|
return New("resource not found").WithCode(NotFoundCode).WithCause(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnknownError ...
|
||||||
|
func UnknownError(err error) *Error {
|
||||||
|
return New("unknown").WithCode(GeneralCode).WithCause(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotFoundErr returns true when the error is NotFoundError.
|
||||||
|
func IsNotFoundErr(err error) bool {
|
||||||
|
return IsErr(err, NotFoundCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRateLimitError checks whether the err chains contains rate limit error.
|
||||||
|
func IsRateLimitError(err error) bool {
|
||||||
|
return IsErr(err, RateLimitCode)
|
||||||
|
}
|
207
registry/app/common/lib/errors/errors.go
Normal file
207
registry/app/common/lib/errors/errors.go
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// As alias function of `errors.As`.
|
||||||
|
As = errors.As
|
||||||
|
// Is alias function of `errors.Is`.
|
||||||
|
Is = errors.Is
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error ...
|
||||||
|
type Error struct {
|
||||||
|
Cause error `json:"-"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Stack *stack `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a human readable error, error.Error() will not
|
||||||
|
// contains the track information. Needs it? just call error.StackTrace()
|
||||||
|
// Code will not be in the error output.
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
out := e.Message
|
||||||
|
if e.Cause != nil {
|
||||||
|
out = out + ": " + e.Cause.Error()
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackTrace ...
|
||||||
|
func (e *Error) StackTrace() string {
|
||||||
|
return e.Stack.frames().format()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON ...
|
||||||
|
func (e *Error) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(
|
||||||
|
&struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}{
|
||||||
|
Code: e.Code,
|
||||||
|
Message: e.Error(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMessage ...
|
||||||
|
func (e *Error) WithMessage(format string, v ...interface{}) *Error {
|
||||||
|
e.Message = fmt.Sprintf(format, v...)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCode ...
|
||||||
|
func (e *Error) WithCode(code string) *Error {
|
||||||
|
e.Code = code
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCause ...
|
||||||
|
func (e *Error) WithCause(err error) *Error {
|
||||||
|
e.Cause = err
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap ...
|
||||||
|
func (e *Error) Unwrap() error { return e.Cause }
|
||||||
|
|
||||||
|
// Errors ...
|
||||||
|
type Errors []error
|
||||||
|
|
||||||
|
var _ error = Errors{}
|
||||||
|
|
||||||
|
// Error converts slice of error.
|
||||||
|
func (errs Errors) Error() string {
|
||||||
|
var tmpErrs struct {
|
||||||
|
Errors []Error `json:"errors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
var err *Error
|
||||||
|
ok := errors.As(e, &err)
|
||||||
|
if !ok {
|
||||||
|
err = UnknownError(e)
|
||||||
|
}
|
||||||
|
if err.Code == "" {
|
||||||
|
err.Code = GeneralCode
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpErrs.Errors = append(tmpErrs.Errors, *err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := json.Marshal(tmpErrs)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Stack().Err(err).Msg("")
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
return string(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the current number of errors.
|
||||||
|
func (errs Errors) Len() int {
|
||||||
|
return len(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrs ...
|
||||||
|
func NewErrs(err error) Errors {
|
||||||
|
return Errors{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New ...
|
||||||
|
func New(in interface{}) *Error {
|
||||||
|
var err error
|
||||||
|
switch in := in.(type) {
|
||||||
|
case error:
|
||||||
|
err = in
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("%v", in)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Error{
|
||||||
|
Message: err.Error(),
|
||||||
|
Stack: newStack(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap ...
|
||||||
|
func Wrap(err error, message string) *Error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e := &Error{
|
||||||
|
Cause: err,
|
||||||
|
Message: message,
|
||||||
|
Stack: newStack(),
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapf ...
|
||||||
|
func Wrapf(err error, format string, args ...interface{}) *Error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e := &Error{
|
||||||
|
Cause: err,
|
||||||
|
Message: fmt.Sprintf(format, args...),
|
||||||
|
Stack: newStack(),
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf ...
|
||||||
|
func Errorf(format string, args ...interface{}) *Error {
|
||||||
|
return &Error{
|
||||||
|
Message: fmt.Sprintf(format, args...),
|
||||||
|
Stack: newStack(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErr checks whether the err chain contains error matches the code.
|
||||||
|
func IsErr(err error, code string) bool {
|
||||||
|
var e *Error
|
||||||
|
if As(err, &e) {
|
||||||
|
return e.Code == code
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCode returns code of err.
|
||||||
|
func ErrCode(err error) string {
|
||||||
|
if err == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var e *Error
|
||||||
|
if ok := As(err, &e); ok && e.Code != "" {
|
||||||
|
return e.Code
|
||||||
|
} else if ok && e.Cause != nil {
|
||||||
|
return ErrCode(e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
return GeneralCode
|
||||||
|
}
|
63
registry/app/common/lib/errors/stack.go
Normal file
63
registry/app/common/lib/errors/stack.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxDepth = 50
|
||||||
|
|
||||||
|
type stack []uintptr
|
||||||
|
|
||||||
|
func (s *stack) frames() StackFrames {
|
||||||
|
var stackFrames StackFrames
|
||||||
|
frames := runtime.CallersFrames(*s)
|
||||||
|
for {
|
||||||
|
frame, next := frames.Next()
|
||||||
|
// filter out runtime
|
||||||
|
if !strings.Contains(frame.File, "runtime/") {
|
||||||
|
stackFrames = append(stackFrames, frame)
|
||||||
|
}
|
||||||
|
if !next {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stackFrames
|
||||||
|
}
|
||||||
|
|
||||||
|
// newStack ...
|
||||||
|
func newStack() *stack {
|
||||||
|
var pcs [maxDepth]uintptr
|
||||||
|
n := runtime.Callers(3, pcs[:])
|
||||||
|
var st stack = pcs[0:n]
|
||||||
|
return &st
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackFrames ...
|
||||||
|
type StackFrames []runtime.Frame
|
||||||
|
|
||||||
|
// Output: <File>:<Line>, <Method>.
|
||||||
|
func (frames StackFrames) format() string {
|
||||||
|
var msg string
|
||||||
|
for _, frame := range frames {
|
||||||
|
msg += fmt.Sprintf("\n%v:%v, %v", frame.File, frame.Line, frame.Function)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
47
registry/app/common/lib/errors/stack_test.go
Normal file
47
registry/app/common/lib/errors/stack_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stackTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *stackTestSuite) SetupTest() {}
|
||||||
|
|
||||||
|
func (c *stackTestSuite) TestFrame() {
|
||||||
|
stack := newStack()
|
||||||
|
frames := stack.frames()
|
||||||
|
c.Equal(len(frames), 4)
|
||||||
|
log.Info().Msg(frames.format())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *stackTestSuite) TestFormat() {
|
||||||
|
stack := newStack()
|
||||||
|
frames := stack.frames()
|
||||||
|
c.Contains(frames[len(frames)-1].Function, "testing.tRunner")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStackTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &stackTestSuite{})
|
||||||
|
}
|
92
registry/app/common/lib/link.go
Normal file
92
registry/app/common/lib/link.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Source: https://github.com/goharbor/harbor
|
||||||
|
|
||||||
|
// Copyright 2016 Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Link defines the model that describes the HTTP link header.
|
||||||
|
type Link struct {
|
||||||
|
URL string
|
||||||
|
Rel string
|
||||||
|
Attrs map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of a link.
|
||||||
|
func (l *Link) String() string {
|
||||||
|
s := fmt.Sprintf("<%s>", l.URL)
|
||||||
|
if len(l.Rel) > 0 {
|
||||||
|
s = fmt.Sprintf(`%s; rel="%s"`, s, l.Rel)
|
||||||
|
}
|
||||||
|
for key, value := range l.Attrs {
|
||||||
|
s = fmt.Sprintf(`%s; %s="%s"`, s, key, value)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Links is a link object array.
|
||||||
|
type Links []*Link
|
||||||
|
|
||||||
|
// String returns the string representation of links.
|
||||||
|
func (l Links) String() string {
|
||||||
|
var strs []string
|
||||||
|
for _, link := range l {
|
||||||
|
strs = append(strs, link.String())
|
||||||
|
}
|
||||||
|
return strings.Join(strs, " , ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLinks parses the link header into Links
|
||||||
|
// e.g. <http://example.com/TheBook/chapter2>; rel="previous";
|
||||||
|
// title="previous chapter" , <http://example.com/TheBook/chapter4>; rel="next"; title="next chapter".
|
||||||
|
func ParseLinks(str string) Links {
|
||||||
|
var links Links
|
||||||
|
for _, lk := range strings.Split(str, ",") {
|
||||||
|
link := &Link{
|
||||||
|
Attrs: map[string]string{},
|
||||||
|
}
|
||||||
|
for _, attr := range strings.Split(lk, ";") {
|
||||||
|
attr = strings.TrimSpace(attr)
|
||||||
|
if len(attr) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if attr[0] == '<' && attr[len(attr)-1] == '>' {
|
||||||
|
link.URL = attr[1 : len(attr)-1]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(attr, "=", 2)
|
||||||
|
key := parts[0]
|
||||||
|
value := ""
|
||||||
|
if len(parts) == 2 {
|
||||||
|
value = strings.Trim(parts[1], `"`)
|
||||||
|
}
|
||||||
|
if key == "rel" {
|
||||||
|
link.Rel = value
|
||||||
|
} else {
|
||||||
|
link.Attrs[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(link.URL) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
links = append(links, link)
|
||||||
|
}
|
||||||
|
return links
|
||||||
|
}
|
31
registry/app/common/url_utils.go
Normal file
31
registry/app/common/url_utils.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2023 Harness, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateOciTokenURL(registryURL string) string {
|
||||||
|
return registryURL + "/v2/token"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateSetupClientHostname(registryURL string) string {
|
||||||
|
regURL, err := url.Parse(registryURL)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return regURL.Host
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user