0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-30 22:34:13 -05:00

ext: use distribution spec route prefix for extension api

Following the spec defined here https://github.com/opencontainers/distribution-spec/tree/main/extensions

Signed-off-by: Shivam Mishra <shimish2@cisco.com>
This commit is contained in:
Shivam Mishra 2022-02-24 12:31:36 -08:00 committed by Ramkumar Chinchani
parent c1bf4456d0
commit 36c9631000
50 changed files with 1076 additions and 395 deletions

View file

@ -56,7 +56,7 @@ jobs:
- name: Install other dependencies
run: |
cd $GITHUB_WORKSPACE
go install github.com/swaggo/swag/cmd/swag@latest
go install github.com/swaggo/swag/cmd/swag@v1.6.3
sudo apt-get update
sudo apt-get install rpm
sudo apt-get install snapd

View file

@ -102,11 +102,11 @@ check: ./golangcilint.yaml $(GOLINTER)
$(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags stress,extended,containers_image_openpgp ./...
swagger/docs.go:
swag -v || go install github.com/swaggo/swag/cmd/swag
swag -v || go install github.com/swaggo/swag/cmd/swag@1.6.3
swag init -o swagger -g pkg/api/routes.go
.PHONY: swagger
swagger: swagger/docs.go
swagger: swagger/docs.go pkg/api/routes.go
.PHONY: update-licenses
update-licenses:

View file

@ -20,6 +20,7 @@ import (
jsoniter "github.com/json-iterator/go"
godigest "github.com/opencontainers/go-digest"
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api/constants"
)
const (
@ -323,7 +324,7 @@ func GetCatalog(workdir, url, auth, repo string, requests int, config testConfig
}()
// send request and get response
resp, err := client.R().Get(url + "/v2/_catalog")
resp, err := client.R().Get(url + constants.RoutePrefix + constants.ExtCatalogPrefix)
latency = time.Since(start)

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
@ -11,31 +11,34 @@
"level": "debug"
},
"extensions": {
"metrics": {
},
"metrics": {},
"sync": {
"credentialsFile": "./examples/sync-auth-filepath.json",
"registries": [{
"urls": ["https://registry1:5000"],
"onDemand": false,
"pollInterval": "6h",
"tlsVerify": true,
"certDir": "/home/user/certs",
"maxRetries": 3,
"retryDelay": "15m",
"content":[
{
"prefix":"/repo1/repo",
"tags":{
"regex":"4.*",
"semver":true
"registries": [
{
"urls": [
"https://registry1:5000"
],
"onDemand": false,
"pollInterval": "6h",
"tlsVerify": true,
"certDir": "/home/user/certs",
"maxRetries": 3,
"retryDelay": "15m",
"content": [
{
"prefix": "/repo1/repo",
"tags": {
"regex": "4.*",
"semver": true
}
},
{
"prefix": "/repo2/repo"
}
},
{
"prefix":"/repo2/repo"
}
]
}]
]
}
]
},
"search": {
"cve": {

View file

@ -1,11 +1,11 @@
{
"distSpecVersion":"1.0.1",
"storage":{
"rootDirectory":"/tmp/zot"
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address":"127.0.0.1",
"port":"8080",
"address": "127.0.0.1",
"port": "8080",
"auth": {
"bearer": {
"realm": "https://auth.myreg.io/auth/token",
@ -14,7 +14,7 @@
}
}
},
"log":{
"level":"debug"
"log": {
"level": "debug"
}
}

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot",
"commit": true

View file

@ -1,15 +1,15 @@
{
"distSpecVersion":"1.0.1",
"storage":{
"rootDirectory":"/tmp/zot",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot",
"gc": false,
"dedupe": false
},
"http": {
"address":"0.0.0.0",
"port":"8080"
"address": "0.0.0.0",
"port": "8080"
},
"log":{
"level":"debug"
"log": {
"level": "debug"
}
}

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},

View file

@ -1,30 +1,39 @@
{
"distSpecVersion": "1.0.1",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address": "127.0.0.1",
"port": "8080",
"realm": "zot",
"accessControl": {
"**": {
"defaultPolicy": ["read", "create"]
},
"tmp/**": {
"defaultPolicy": ["read", "create", "update"]
},
"infra/**": {
"defaultPolicy": ["read"]
},
"repos2/repo": {
"defaultPolicy": ["read"]
}
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address": "127.0.0.1",
"port": "8080",
"realm": "zot",
"accessControl": {
"**": {
"defaultPolicy": [
"read",
"create"
]
},
"tmp/**": {
"defaultPolicy": [
"read",
"create",
"update"
]
},
"infra/**": {
"defaultPolicy": [
"read"
]
},
"repos2/repo": {
"defaultPolicy": [
"read"
]
}
},
"log": {
"level": "debug"
}
},
"log": {
"level": "debug"
}
}

View file

@ -1,27 +1,27 @@
{
"distSpecVersion":"1.0.1",
"storage":{
"rootDirectory":"/tmp/zot"
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address":"127.0.0.1",
"port":"8080",
"realm":"zot",
"address": "127.0.0.1",
"port": "8080",
"realm": "zot",
"tls": {
"cert":"test/data/server.cert",
"key":"test/data/server.key"
"cert": "test/data/server.cert",
"key": "test/data/server.key"
},
"auth": {
"ldap": {
"address":"ldap.example.org",
"port":389,
"startTLS":false,
"baseDN":"ou=Users,dc=example,dc=org",
"userAttribute":"uid",
"bindDN":"cn=ldap-searcher,ou=Users,dc=example,dc=org",
"bindPassword":"ldap-searcher-password",
"skipVerify":false,
"subtreeSearch":true
"address": "ldap.example.org",
"port": 389,
"startTLS": false,
"baseDN": "ou=Users,dc=example,dc=org",
"userAttribute": "uid",
"bindDN": "cn=ldap-searcher,ou=Users,dc=example,dc=org",
"bindPassword": "ldap-searcher-password",
"skipVerify": false,
"subtreeSearch": true
},
"htpasswd": {
"path": "test/data/htpasswd"
@ -30,9 +30,9 @@
},
"allowReadAccess": false
},
"log":{
"level":"debug",
"output":"/tmp/zot.log",
"log": {
"level": "debug",
"output": "/tmp/zot.log",
"audit": "/tmp/zot-audit.log"
}
}

View file

@ -1,4 +1,4 @@
distspecversion: 1.0.1
distspecversion: 1.0.1-dev
http:
address: 127.0.0.1
allowreadaccess: false

View file

@ -1,5 +1,5 @@
{
"distSpecVersion":"1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot",
"gc": true,

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot",
"gc": true,

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot",
"dedupe": true,

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot",
"dedupe": true,

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
@ -17,44 +17,91 @@
"**": {
"policies": [
{
"users": ["charlie"],
"actions": ["read", "create", "update"]
"users": [
"charlie"
],
"actions": [
"read",
"create",
"update"
]
}
],
"defaultPolicy": ["read", "create"]
"defaultPolicy": [
"read",
"create"
]
},
"tmp/**": {
"defaultPolicy": ["read", "create", "update"]
"defaultPolicy": [
"read",
"create",
"update"
]
},
"infra/**": {
"policies": [
{
"users": ["alice", "bob"],
"actions": ["create", "read", "update", "delete"]
"users": [
"alice",
"bob"
],
"actions": [
"create",
"read",
"update",
"delete"
]
},
{
"users": ["mallory"],
"actions": ["create", "read"]
"users": [
"mallory"
],
"actions": [
"create",
"read"
]
}
],
"defaultPolicy": ["read"]
"defaultPolicy": [
"read"
]
},
"repos2/repo": {
"policies": [
{
"users": ["charlie"],
"actions": ["read", "create"]
"users": [
"charlie"
],
"actions": [
"read",
"create"
]
},
{
"users": ["mallory"],
"actions": ["create", "read"]
"users": [
"mallory"
],
"actions": [
"create",
"read"
]
}
],
"defaultPolicy": ["read"]
"defaultPolicy": [
"read"
]
},
"adminPolicy": {
"users": ["admin"],
"actions": ["read", "create", "update", "delete"]
"users": [
"admin"
],
"actions": [
"read",
"create",
"update",
"delete"
]
}
}
},
@ -63,4 +110,3 @@
"output": "/tmp/zot.log"
}
}

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},

View file

@ -1,5 +1,5 @@
{
"distSpecVersion": "1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/zot",
"storageDriver": {

View file

@ -1,5 +1,5 @@
{
"distSpecVersion":"1.0.1",
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},

View file

@ -1,68 +1,76 @@
{
"distSpecVersion":"1.0.1",
"storage":{
"rootDirectory":"/tmp/zot"
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http":{
"address":"127.0.0.1",
"port":"8080"
"http": {
"address": "127.0.0.1",
"port": "8080"
},
"log":{
"level":"debug"
"log": {
"level": "debug"
},
"extensions":{
"extensions": {
"sync": {
"enable": true,
"credentialsFile": "./examples/sync-auth-filepath.json",
"registries": [{
"urls": ["https://registry1:5000"],
"onDemand": false,
"pollInterval": "6h",
"tlsVerify": true,
"certDir": "/home/user/certs",
"maxRetries": 3,
"retryDelay": "5m",
"onlySigned": true,
"content":[
{
"prefix":"/repo1/repo",
"tags":{
"regex":"4.*",
"semver":true
"registries": [
{
"urls": [
"https://registry1:5000"
],
"onDemand": false,
"pollInterval": "6h",
"tlsVerify": true,
"certDir": "/home/user/certs",
"maxRetries": 3,
"retryDelay": "5m",
"onlySigned": true,
"content": [
{
"prefix": "/repo1/repo",
"tags": {
"regex": "4.*",
"semver": true
}
},
{
"prefix": "/repo1/repo",
"destination": "/repo",
"stripPrefix": true
},
{
"prefix": "/repo2/repo"
}
},
{
"prefix":"/repo1/repo",
"destination": "/repo",
"stripPrefix": true
},
{
"prefix":"/repo2/repo"
}
]
},
{
"urls": ["https://registry2:5000", "https://registry3:5000"],
"pollInterval": "12h",
"tlsVerify": false,
"onDemand": false,
"content":[
{
"prefix":"/repo2",
"tags":{
"semver":true
]
},
{
"urls": [
"https://registry2:5000",
"https://registry3:5000"
],
"pollInterval": "12h",
"tlsVerify": false,
"onDemand": false,
"content": [
{
"prefix": "/repo2",
"tags": {
"semver": true
}
}
}
]
},
{
"urls": ["https://docker.io/library"],
"onDemand": true,
"tlsVerify": true,
"maxRetries": 6,
"retryDelay": "5m"
}
]
]
},
{
"urls": [
"https://docker.io/library"
],
"onDemand": true,
"tlsVerify": true,
"maxRetries": 6,
"retryDelay": "5m"
}
]
}
}
}

View file

@ -1,13 +1,13 @@
{
"distSpecVersion":"1.0.1",
"storage":{
"rootDirectory":"/tmp/zot"
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address":"127.0.0.1",
"port":"8080"
"address": "127.0.0.1",
"port": "8080"
},
"log":{
"level":"debug"
"log": {
"level": "debug"
}
}

View file

@ -1,18 +1,18 @@
{
"distSpecVersion":"1.0.1",
"storage":{
"rootDirectory":"/tmp/zot"
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address":"127.0.0.1",
"port":"8080",
"realm":"zot",
"address": "127.0.0.1",
"port": "8080",
"realm": "zot",
"tls": {
"cert":"test/data/server.cert",
"key":"test/data/server.key"
"cert": "test/data/server.cert",
"key": "test/data/server.key"
}
},
"log":{
"level":"debug"
"log": {
"level": "debug"
}
}

17
go.mod
View file

@ -47,7 +47,6 @@ require (
github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.11.0
github.com/stretchr/testify v1.7.1
github.com/swaggo/http-swagger v1.2.6
github.com/swaggo/swag v1.8.1
github.com/urfave/cli/v2 v2.4.0
github.com/vektah/gqlparser/v2 v2.4.1
@ -57,9 +56,11 @@ require (
gopkg.in/yaml.v2 v2.4.0
)
require github.com/open-policy-agent/opa v0.37.0 // indirect
require (
github.com/open-policy-agent/opa v0.37.0 // indirect
github.com/opencontainers/distribution-spec v1.0.1
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220217185014-dd38b7ed8a99
github.com/swaggo/http-swagger v1.2.8
)
require (
@ -90,8 +91,6 @@ require (
github.com/PaesslerAG/gval v1.0.0 // indirect
github.com/PaesslerAG/jsonpath v0.1.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
@ -179,10 +178,10 @@ require (
github.com/go-openapi/analysis v0.21.2 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/loads v0.21.1 // indirect
github.com/go-openapi/runtime v0.23.3 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/spec v0.20.6 // indirect
github.com/go-openapi/strfmt v0.21.2 // indirect
github.com/go-openapi/swag v0.21.1 // indirect
github.com/go-openapi/validate v0.21.0 // indirect
@ -361,10 +360,10 @@ require (
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect

25
go.sum
View file

@ -303,10 +303,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C6
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/ReneKroon/ttlcache/v2 v2.10.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM=
@ -1068,8 +1066,9 @@ github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
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/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
@ -1103,8 +1102,10 @@ github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFu
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ=
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/spec v0.20.5/go.mod h1:QbfOSIVt3/sac+a1wzmKbbcLXm5NdZnyBZYtCijp43o=
github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
@ -2142,8 +2143,8 @@ github.com/open-policy-agent/opa v0.32.0/go.mod h1:5sJdtc+1/U8zy/j30njpQl6u9rM4M
github.com/open-policy-agent/opa v0.35.0/go.mod h1:xEmekKlk6/c+so5HF9wtPnGPXDfBuBsrMGhSHOHEF+U=
github.com/open-policy-agent/opa v0.37.0 h1:OUXB+RAcxQpmXeNW2BN1wYzQQvVCPF1T9zv+QXGr9Wg=
github.com/open-policy-agent/opa v0.37.0/go.mod h1:xX3NUCZuXK8f0CNhFQvhm4495mZLptf94pIkWRLaFqo=
github.com/opencontainers/distribution-spec v1.0.1 h1:hT6tF6uKZAQh+HH3BrJqn7/xHhMoDHzahIg4KxD2DqM=
github.com/opencontainers/distribution-spec v1.0.1/go.mod h1:copR2flp+jTEvQIFMb6MIx45OkrxzqyjszPDT3hx/5Q=
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220217185014-dd38b7ed8a99 h1:yGqpYh1h3ZXxGg4Mqd5ZwhlwQ9wXOgvuodxbxJA7iMY=
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220217185014-dd38b7ed8a99/go.mod h1:aA4vdXRS8E1TG7pLZOz85InHi3BiPdErh8IpJN6E0x4=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@ -2546,9 +2547,8 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/http-swagger v1.2.6 h1:ihTjChUoSRMpFMjWw+0AkL1Ti4r6v8pCgVYLmQVRlRw=
github.com/swaggo/http-swagger v1.2.6/go.mod h1:CcoICgY3yVDk2u1LQUCMHbAj0fjlxIX+873psXlIKNA=
github.com/swaggo/swag v1.7.9/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU=
github.com/swaggo/http-swagger v1.2.8 h1:TVjxLU7qoqofJ9qynJazmpTGs/p4Kx9FTp7YYwOkJb0=
github.com/swaggo/http-swagger v1.2.8/go.mod h1:FrQwV7rx+A5t11PIX8d+tFJa2GKx11RdAXQptllPQHg=
github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI=
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -3099,8 +3099,10 @@ golang.org/x/net v0.0.0-20220127074510-2fabfed7e28f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -3310,8 +3312,9 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View file

@ -3,6 +3,7 @@ package api
import (
"context"
"encoding/base64"
"fmt"
"net/http"
"strings"
"time"
@ -10,6 +11,7 @@ import (
glob "github.com/bmatcuk/doublestar/v4"
"github.com/gorilla/mux"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/common"
"zotregistry.io/zot/pkg/log"
)
@ -191,7 +193,7 @@ func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
ctx := acCtrlr.getContext(username, request)
// will return only repos on which client is authorized to read
if request.RequestURI == "/v2/_catalog" {
if request.RequestURI == fmt.Sprintf("%s%s", constants.RoutePrefix, constants.ExtCatalogPrefix) {
next.ServeHTTP(response, request.WithContext(ctx))
return

View file

@ -1,11 +1,12 @@
package constants
const (
ArtifactSpecRoutePrefix = "/oras/artifacts/v1"
RoutePrefix = "/v2"
DistAPIVersion = "Docker-Distribution-API-Version"
DistContentDigestKey = "Docker-Content-Digest"
BlobUploadUUID = "Blob-Upload-UUID"
DefaultMediaType = "application/json"
BinaryMediaType = "application/octet-stream"
ArtifactSpecRoutePrefix = "/oras/artifacts/v1"
RoutePrefix = "/v2"
DistAPIVersion = "Docker-Distribution-API-Version"
DistContentDigestKey = "Docker-Content-Digest"
BlobUploadUUID = "Blob-Upload-UUID"
DefaultMediaType = "application/json"
BinaryMediaType = "application/octet-stream"
DefaultMetricsExtensionRoute = "/metrics"
)

View file

@ -0,0 +1,9 @@
package constants
// https://github.com/opencontainers/distribution-spec/tree/main/extensions#extensions-api-for-distribution
const (
ExtCatalogPrefix = "/_catalog"
ExtOciDiscoverPrefix = "/_oci/ext/discover"
// zot specific extensions.
ExtSearchPrefix = RoutePrefix + "/_search"
)

View file

@ -32,6 +32,7 @@ import (
"github.com/mitchellh/mapstructure"
vldap "github.com/nmcclain/ldap"
notreg "github.com/notaryproject/notation/pkg/registry"
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
@ -47,6 +48,7 @@ import (
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/test"
)
@ -1878,7 +1880,7 @@ func TestAuthorizationWithBasicAuth(t *testing.T) {
// everybody should have access to /v2/_catalog
resp, err = resty.R().SetBasicAuth(username, passphrase).
Get(baseURL + "/v2/_catalog")
Get(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
@ -3195,7 +3197,7 @@ func TestParallelRequests(t *testing.T) {
assert.Equal(t, tagResponse.StatusCode(), http.StatusOK, "response status code should return success code")
repoResponse, err := client.R().SetBasicAuth(username, passphrase).
Get(baseURL + "/v2/_catalog")
Get(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
assert.Equal(t, err, nil, "Error should be nil")
assert.Equal(t, repoResponse.StatusCode(), http.StatusOK, "response status code should return success code")
})
@ -4812,6 +4814,80 @@ func TestPeriodicGC(t *testing.T) {
})
}
func TestDistSpecExtensions(t *testing.T) {
Convey("start zot server with search extension", t, func(c C) {
conf := config.New()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf.HTTP.Port = port
defaultVal := true
searchConfig := &extconf.SearchConfig{
Enable: &defaultVal,
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
logFile, err := ioutil.TempFile("", "zot-log*.txt")
So(err, ShouldBeNil)
conf.Log.Output = logFile.Name()
defer os.Remove(logFile.Name()) // clean up
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
go startServer(ctlr)
defer stopServer(ctlr)
test.WaitTillServerReady(baseURL)
var extensionList distext.ExtensionList
resp, err := resty.R().Get(baseURL + constants.RoutePrefix + constants.ExtOciDiscoverPrefix)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
err = json.Unmarshal(resp.Body(), &extensionList)
So(err, ShouldBeNil)
So(len(extensionList.Extensions), ShouldEqual, 1)
})
Convey("start minimal zot server", t, func(c C) {
conf := config.New()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf.HTTP.Port = port
logFile, err := ioutil.TempFile("", "zot-log*.txt")
So(err, ShouldBeNil)
conf.Log.Output = logFile.Name()
defer os.Remove(logFile.Name()) // clean up
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
go startServer(ctlr)
defer stopServer(ctlr)
test.WaitTillServerReady(baseURL)
var extensionList distext.ExtensionList
resp, err := resty.R().Get(baseURL + constants.RoutePrefix + constants.ExtOciDiscoverPrefix)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
err = json.Unmarshal(resp.Body(), &extensionList)
So(err, ShouldBeNil)
So(len(extensionList.Extensions), ShouldEqual, 0)
})
}
func getAllBlobs(imagePath string) []string {
blobList := make([]string, 0)

View file

@ -25,6 +25,7 @@ import (
"github.com/gorilla/mux"
jsoniter "github.com/json-iterator/go"
notreg "github.com/notaryproject/notation/pkg/registry"
"github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
httpSwagger "github.com/swaggo/http-swagger"
@ -69,6 +70,7 @@ func (rh *RouteHandler) SetupRoutes() {
rh.c.Router.Use(AuthzHandler(rh.c))
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints
prefixedRouter := rh.c.Router.PathPrefix(constants.RoutePrefix).Subrouter()
{
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", NameRegexp.String()),
@ -97,8 +99,10 @@ func (rh *RouteHandler) SetupRoutes() {
rh.UpdateBlobUpload).Methods("PUT")
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()),
rh.DeleteBlobUpload).Methods("DELETE")
prefixedRouter.HandleFunc("/_catalog",
prefixedRouter.HandleFunc(constants.ExtCatalogPrefix,
rh.ListRepositories).Methods(allowedMethods("GET")...)
prefixedRouter.HandleFunc(constants.ExtOciDiscoverPrefix,
rh.ListExtensions).Methods(allowedMethods("GET")...)
prefixedRouter.HandleFunc("/",
rh.CheckVersionSupport).Methods(allowedMethods("GET")...)
}
@ -336,6 +340,10 @@ type ImageManifest struct {
ispec.Manifest
}
type ExtensionList struct {
extensions.ExtensionList
}
// GetManifest godoc
// @Summary Get image manifest
// @Description Get an image's manifest given a reference or a digest
@ -1249,6 +1257,19 @@ func (rh *RouteHandler) ListRepositories(response http.ResponseWriter, request *
WriteJSON(response, http.StatusOK, is)
}
// ListExtensions godoc
// @Summary List Registry level extensions
// @Description List all extensions present on registry
// @Accept json
// @Produce json
// @Success 200 {object} api.ExtensionList
// @Router /v2/_oci/ext/discover [get].
func (rh *RouteHandler) ListExtensions(w http.ResponseWriter, r *http.Request) {
extensionList := ext.GetExtensions(rh.c.Config)
WriteJSON(w, http.StatusOK, extensionList)
}
func (rh *RouteHandler) GetMetrics(w http.ResponseWriter, r *http.Request) {
m := rh.c.Metrics.ReceiveMetrics()
WriteJSON(w, http.StatusOK, m)

View file

@ -20,6 +20,7 @@ import (
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
)
func TestElevatedPrivilegesTLSNewControllerPrivilegedCert(t *testing.T) {
@ -106,8 +107,8 @@ func TestElevatedPrivilegesTLSNewControllerPrivilegedCert(t *testing.T) {
Convey("Certs in privileged path", func() {
configPath := makeConfigFile(
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s/v2/_catalog","showspinner":false}]}`,
BaseSecureURL2))
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`,
BaseSecureURL2, constants.RoutePrefix, constants.ExtCatalogPrefix))
defer os.Remove(configPath)
args := []string{"imagetest"}

View file

@ -19,6 +19,7 @@ import (
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/test"
)
@ -114,8 +115,8 @@ func TestTLSWithAuth(t *testing.T) {
args = []string{"imagetest"}
configPath = makeConfigFile(
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s/v2/_catalog","showspinner":false}]}`,
BaseSecureURL1))
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`,
BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix))
defer os.Remove(configPath)
imageCmd = NewImageCommand(new(searchService))
imageBuff = bytes.NewBufferString("")
@ -129,8 +130,8 @@ func TestTLSWithAuth(t *testing.T) {
user := fmt.Sprintf("%s:%s", username, passphrase)
args = []string{"imagetest", "-u", user}
configPath = makeConfigFile(
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s/v2/_catalog","showspinner":false}]}`,
BaseSecureURL1))
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`,
BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix))
defer os.Remove(configPath)
imageCmd = NewImageCommand(new(searchService))
imageBuff = bytes.NewBufferString("")
@ -185,8 +186,8 @@ func TestTLSWithoutAuth(t *testing.T) {
Convey("Certs in user's home", func() {
configPath := makeConfigFile(
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s/v2/_catalog","showspinner":false}]}`,
BaseSecureURL1))
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`,
BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix))
defer os.Remove(configPath)
home := os.Getenv("HOME")
@ -250,8 +251,8 @@ func TestTLSBadCerts(t *testing.T) {
Convey("Test with system certs", func() {
configPath := makeConfigFile(
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s/v2/_catalog","showspinner":false}]}`,
BaseSecureURL3))
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`,
BaseSecureURL3, constants.RoutePrefix, constants.ExtCatalogPrefix))
defer os.Remove(configPath)
args := []string{"imagetest"}

View file

@ -5,7 +5,6 @@ package cli //nolint:testpackage
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"strings"
@ -216,8 +215,6 @@ func TestConfigCmdMain(t *testing.T) {
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
fmt.Println(err)
fmt.Println(buff.String())
So(buff.String(), ShouldContainSubstring, "does not exist")
})
})
@ -254,8 +251,6 @@ func TestConfigCmdMain(t *testing.T) {
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
fmt.Println(err)
fmt.Println(buff.String())
So(buff.String(), ShouldContainSubstring, "does not exist")
})
})

View file

@ -19,6 +19,7 @@ import (
zotErrors "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/test"
)
@ -320,7 +321,7 @@ func TestServerCVEResponse(t *testing.T) {
}(ctlr)
// wait till ready
for {
res, err := resty.R().Get(url + "/query")
res, err := resty.R().Get(url + constants.ExtSearchPrefix)
if err == nil && res.StatusCode() == 200 {
break
}

View file

@ -16,6 +16,7 @@ import (
"zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring"
"zotregistry.io/zot/pkg/storage"
@ -332,7 +333,7 @@ func applyDefaultValues(config *config.Config, viperInstance *viper.Viper) {
}
if config.Extensions.Metrics.Prometheus == nil {
config.Extensions.Metrics.Prometheus = &extconf.PrometheusConfig{Path: "/metrics"}
config.Extensions.Metrics.Prometheus = &extconf.PrometheusConfig{Path: constants.DefaultMetricsExtensionRoute}
}
}
}

View file

@ -18,6 +18,7 @@ import (
"github.com/olekukonko/tablewriter"
"gopkg.in/yaml.v2"
zotErrors "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api/constants"
)
type SearchService interface {
@ -70,7 +71,8 @@ func (service searchService) getAllImages(ctx context.Context, config searchConf
catalog := &catalogResponse{}
catalogEndPoint, err := combineServerAndEndpointURL(*config.servURL, "/v2/_catalog")
catalogEndPoint, err := combineServerAndEndpointURL(*config.servURL, fmt.Sprintf("%s%s",
constants.RoutePrefix, constants.ExtCatalogPrefix))
if err != nil {
if isContextDone(ctx) {
return
@ -453,7 +455,7 @@ func (service searchService) makeGraphQLQuery(ctx context.Context, config search
username, password, query string,
resultPtr interface{},
) error {
endPoint, err := combineServerAndEndpointURL(*config.servURL, "/query")
endPoint, err := combineServerAndEndpointURL(*config.servURL, constants.ExtSearchPrefix)
if err != nil {
return err
}

View file

@ -50,14 +50,14 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
Convey("Make API calls to the controller", t, func(c C) {
Convey("Check version", func() {
_, _ = Print("\nCheck version")
resp, err := resty.R().Get(baseURL + "/v2/")
resp, err := resty.R().Get(baseURL + constants.RoutePrefix + "/")
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
})
Convey("Get repository catalog", func() {
_, _ = Print("\nGet repository catalog")
resp, err := resty.R().Get(baseURL + "/v2/_catalog")
resp, err := resty.R().Get(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
So(resp.String(), ShouldNotBeEmpty)
@ -77,7 +77,8 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
resp, err = resty.R().SetResult(&api.RepositoryList{}).Get(baseURL + "/v2/_catalog")
resp, err = resty.R().SetResult(&api.RepositoryList{}).Get(baseURL +
constants.RoutePrefix + constants.ExtCatalogPrefix)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
So(resp.String(), ShouldNotBeEmpty)

194
pkg/extensions/_search.md Normal file
View file

@ -0,0 +1,194 @@
`search` extension
===
`search` extension provides efficient and enhanced registry search capabilities using graphQL backend.
Table of Contents
===
| Supported queries | Input | Ouput | Description | graphQL query |
| --- | --- | --- | --- | --- |
| [Search images by digest](#search-images-by-digest) | digest | image list | Search all repositories in the registry and return list of images that matches given digest (manifest, config or layers) | ImageListForDigest |
| [Search images affected by a given CVE id](#search-images-affected-by-a-given-cve-id) | CVE id | image list | Search the entire registry and return list of images affected by given CVE | ImagesListForCVE |
| [List CVEs for a given image](#list-cves-of-given-image) | image | CVE list | Scan given image and return list of CVEs affecting the image | CVEListForImage |
| [List images not affected by a given CVE id](#list-images-not-affected-by-a-given-cve-id) | repository, CVE id | image list | Scan all images in a given repository and return list of latest (by date) images not affected by the given CVE |ImagesListWithCVEFixed|
| [List the latest image across every repository](#list-the-latest-image-across-every-repository) | \<none\> | image list | Search entire registry and return a list containing the latest (by date) image in each repository | ImageListWithLatestTag |
| [List all images with expanded information for a given repository](#list-all-images-with-expanded-information-for-a-given-repository) | repository | image list | List expanded image information for all images (including manifest, all layers, etc) in a given repository | ExpandedRepoInfo |
# Search images by digest
**Sample request**
```
curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageListForDigest (id:\"63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29\") { Name Tags } }" }' http://localhost:8080/v2/_search
```
**Sample response**
```
{
"data": {
"ImageListForDigest": [{
"Name": "centos",
"Tags": ["8"]
}, {
"Name": "v2/centos",
"Tags": ["8"]
}]
}
}
```
# Search images affected by a given CVE id
**Sample request**
```
curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageListForCVE (id:\"CVE-2002-1119\") { Name Tags } }" }' http://localhost:8080/v2/_search
```
**Sample response**
```
{
"data": {
"ImageListForCVE": [{
"Name": "centos",
"Tags": ["8"]
}, {
"Name": "v2/centos",
"Tags": ["7", "8"]
}]
}
}
```
# List CVEs of given image
**Sample reques**t
```
curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ CVEListForImage (image:\"centos\" ) { Tag CVEList { Id Title Description Severity PackageList {Name InstalledVersion FixedVersion } } } }" }' http://localhost:8080/v2/_search
```
**Sample response**
```
{
"data": {
"CVEListForImage": {
"Tag": "",
"CVEList": [{
"Id": "CVE-2021-3712",
"Title": "openssl: Read buffer overruns processing ASN.1 strings",
"Description": "ASN.1 strings are represented internally within OpenSSL as an ASN1_STRING structure which contains a buffer. Fixed in OpenSSL 1.1.1l (Affected 1.1.1-1.1.1k). Fixed in OpenSSL 1.0.2za (Affected 1.0.2-1.0.2y).",
"Severity": "MEDIUM",
"PackageList": [{
"Name": "openssl-libs",
"InstalledVersion": "1:1.1.1g-11.el8",
"FixedVersion": "1:1.1.1k-5.el8_5"
}]
}]
}
}
}
```
# List images not affected by a given CVE id
**Sample request**
```
curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageListWithCVEFixed (id:\"CVE-2021-3713\",image:\"centos\") { Tags {Name Digest Timestamp} } }" }' http://localhost:8080/v2/_search
```
**Sample response**
```
{
"data": {
"ImageListWithCVEFixed": {
"Tags": [{
"Name": "8",
"Digest": "sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29",
"Timestamp": "2020-12-08T00:22:52.526672082Z"
}]
}
}
}
```
# List the latest image across every repository
**Sample request**
```
curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageListWithLatestTag () { Name Latest LastUpdated Description Licenses Vendor Size Labels} }" }' http://localhost:8080/v2/_search
```
**Sample response**
```
{
"data": {
"ImageListWithLatestTag": [{
"Name": "centos",
"Latest": "8",
"LastUpdated": "2020-12-08T00:22:52.526672082Z",
"Description": "",
"Licenses": "GPLv2",
"Vendor": "CentOS",
"Size": "1074",
"Labels": ""
}, {
"Name": "v2/centos",
"Latest": "8",
"LastUpdated": "2020-12-08T00:22:52.526672082Z",
"Description": "",
"Licenses": "GPLv2",
"Vendor": "CentOS",
"Size": "1074",
"Labels": ""
}]
}
}
```
# List all images with expanded information for a given repository
Sample request
```
curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ExpandedRepoInfo (repo:\"v2/centos\") { Manifests {Digest Tag IsSigned Layers {Size Digest}}} }" }' http://localhost:8080/v2/_search
```
**Sample response**
```
{
"data": {
"ExpandedRepoInfo": {
"Manifests": [{
"Digest": "2bacca16b9df395fc855c14ccf50b12b58d35d468b8e7f25758aff90f89bf396",
"Tag": "7",
"IsSigned": false,
"Layers": [{
"Size": "76097157",
"Digest": "2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc"
}]
}, {
"Digest": "63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29",
"Tag": "8",
"IsSigned": false,
"Layers": [{
"Size": "75181999",
"Digest": "7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621"
}]
}]
}
}
}
```
# References
[1] https://github.com/opencontainers/distribution-spec/tree/main/extensions

View file

@ -5,13 +5,16 @@ package extensions
import (
"context"
"fmt"
goSync "sync"
"time"
gqlHandler "github.com/99designs/gqlgen/graphql/handler"
"github.com/gorilla/mux"
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
"github.com/prometheus/client_golang/prometheus/promhttp"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/extensions/scrub"
"zotregistry.io/zot/pkg/extensions/search"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
@ -61,9 +64,10 @@ func EnableExtensions(config *config.Config, log log.Logger, rootDir string) {
*config.Extensions.Metrics.Enable &&
config.Extensions.Metrics.Prometheus != nil {
if config.Extensions.Metrics.Prometheus.Path == "" {
config.Extensions.Metrics.Prometheus.Path = "/metrics"
config.Extensions.Metrics.Prometheus.Path = constants.DefaultMetricsExtensionRoute
log.Warn().Msg("Prometheus instrumentation Path not set, changing to '/metrics'.")
log.Warn().Msg(fmt.Sprintf("Prometheus instrumentation Path not set, changing to %s.",
constants.DefaultMetricsExtensionRoute))
}
} else {
log.Info().Msg("Metrics config not provided, skipping Metrics config update")
@ -108,9 +112,34 @@ func EnableScrubExtension(config *config.Config, storeController storage.StoreCo
}
}
func getExtension(name, url, description string) distext.Extension {
return distext.Extension{
Name: name,
URL: url,
Description: description,
}
}
func GetExtensions(config *config.Config) distext.ExtensionList {
extensionList := distext.ExtensionList{}
extensions := make([]distext.Extension, 0)
if config.Extensions != nil && config.Extensions.Search != nil {
searchExt := getExtension("search",
"https://github.com/project-zot/zot/tree/main/pkg/extensions/search/_search.md",
"search extension to provide various search feature e.g cve")
extensions = append(extensions, searchExt)
}
extensionList.Extensions = extensions
return extensionList
}
// SetupRoutes ...
func SetupRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController,
l log.Logger,
func SetupRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController, l log.Logger,
) {
// fork a new zerolog child to avoid data race
log := log.Logger{Logger: l.With().Caller().Timestamp().Logger()}
@ -125,7 +154,7 @@ func SetupRoutes(config *config.Config, router *mux.Router, storeController stor
resConfig = search.GetResolverConfig(log, storeController, false)
}
router.PathPrefix("/query").Methods("GET", "POST", "OPTIONS").
router.PathPrefix(constants.ExtSearchPrefix).Methods("OPTIONS", "GET", "POST").
Handler(gqlHandler.NewDefaultServer(search.NewExecutableSchema(resConfig)))
}

View file

@ -9,6 +9,7 @@ import (
"time"
"github.com/gorilla/mux"
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
@ -25,6 +26,11 @@ func EnableExtensions(config *config.Config, log log.Logger, rootDir string) {
"any extensions, please build zot full binary for this feature")
}
// GetExtensions...
func GetExtensions(config *config.Config) distext.ExtensionList {
return distext.ExtensionList{}
}
// EnableSyncExtension ...
func EnableSyncExtension(ctx context.Context, config *config.Config, wg *goSync.WaitGroup,
storeController storage.StoreController, log log.Logger,
@ -42,7 +48,8 @@ func EnableScrubExtension(config *config.Config, storeController storage.StoreCo
}
// SetupRoutes ...
func SetupRoutes(conf *config.Config, router *mux.Router, storeController storage.StoreController, log log.Logger) {
func SetupRoutes(conf *config.Config, router *mux.Router, storeController storage.StoreController, log log.Logger,
) {
log.Warn().Msg("skipping setting up extensions routes because given zot binary doesn't support " +
"any extensions, please build zot full binary for this feature")
}

View file

@ -16,6 +16,7 @@ import (
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring"
"zotregistry.io/zot/pkg/extensions/search/common"
@ -24,6 +25,10 @@ import (
. "zotregistry.io/zot/pkg/test"
)
const (
graphqlQueryPrefix = constants.ExtSearchPrefix
)
// nolint:gochecknoglobals
var (
rootDir string
@ -225,12 +230,12 @@ func TestLatestTagSearchHTTP(t *testing.T) {
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(baseURL + "/query")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(baseURL + "/query?query={ImageListWithLatestTag(){Name%20Latest}}")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){Name%20Latest}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -243,7 +248,7 @@ func TestLatestTagSearchHTTP(t *testing.T) {
images := responseStruct.ImgListWithLatestTag.Images
So(images[0].Latest, ShouldEqual, "0.0.1")
resp, err = resty.R().Get(baseURL + "/query?query={ImageListWithLatestTag(){Name%20Latest}}")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){Name%20Latest}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
@ -252,7 +257,7 @@ func TestLatestTagSearchHTTP(t *testing.T) {
panic(err)
}
resp, err = resty.R().Get(baseURL + "/query?query={ImageListWithLatestTag(){Name%20Latest}}")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){Name%20Latest}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -273,7 +278,7 @@ func TestLatestTagSearchHTTP(t *testing.T) {
panic(err)
}
resp, err = resty.R().Get(baseURL + "/query?query={ImageListWithLatestTag(){Name%20Latest}}")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){Name%20Latest}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -284,7 +289,7 @@ func TestLatestTagSearchHTTP(t *testing.T) {
panic(err)
}
resp, err = resty.R().Get(baseURL + "/query?query={ImageListWithLatestTag(){Name%20Latest}}")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){Name%20Latest}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -295,7 +300,7 @@ func TestLatestTagSearchHTTP(t *testing.T) {
panic(err)
}
resp, err = resty.R().Get(baseURL + "/query?query={ImageListWithLatestTag(){Name%20Latest}}")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){Name%20Latest}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -307,7 +312,7 @@ func TestLatestTagSearchHTTP(t *testing.T) {
panic(err)
}
resp, err = resty.R().Get(baseURL + "/query?query={ImageListWithLatestTag(){Name%20Latest}}")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){Name%20Latest}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -363,14 +368,14 @@ func TestExpandedRepoInfo(t *testing.T) {
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(baseURL + "/query")
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
query := "{ExpandedRepoInfo(repo:\"zot-test\"){Manifests%20{Digest%20IsSigned%20Tag%20Layers%20{Size%20Digest}}}}"
resp, err = resty.R().Get(baseURL + "/query?query=" + query)
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + query)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -384,13 +389,13 @@ func TestExpandedRepoInfo(t *testing.T) {
query = "{ExpandedRepoInfo(repo:\"\"){Manifests%20{Digest%20Tag%20IsSigned%20Layers%20{Size%20Digest}}}}"
resp, err = resty.R().Get(baseURL + "/query?query=" + query)
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + query)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
query = "{ExpandedRepoInfo(repo:\"a/zot-test\"){Manifests%20{Digest%20Tag%20IsSigned%20%Layers%20{Size%20Digest}}}}"
resp, err = resty.R().Get(baseURL + "/query?query=" + query)
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + query)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -408,7 +413,7 @@ func TestExpandedRepoInfo(t *testing.T) {
query = "{ExpandedRepoInfo(repo:\"zot-test\"){Manifests%20{Digest%20Tag%20IsSigned%20%Layers%20{Size%20Digest}}}}"
resp, err = resty.R().Get(baseURL + "/query?query=" + query)
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + query)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)

View file

@ -20,6 +20,7 @@ import (
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring"
"zotregistry.io/zot/pkg/extensions/search/common"
@ -430,7 +431,7 @@ func TestCVESearch(t *testing.T) {
err = json.Unmarshal(resp.Body(), &apiErr)
So(err, ShouldBeNil)
resp, err = resty.R().Get(baseURL + "/query/")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 401)
@ -446,11 +447,11 @@ func TestCVESearch(t *testing.T) {
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -461,7 +462,7 @@ func TestCVESearch(t *testing.T) {
cvid := cveResult.ImgList.CVEResultForImage.CVEList[0].ID
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-test\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-test\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -470,7 +471,7 @@ func TestCVESearch(t *testing.T) {
So(err, ShouldBeNil)
So(len(imgFixedCVEResult.ImgResults.ImgResultForFixedCVE.Tags), ShouldEqual, 0)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-cve-test\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-cve-test\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -478,11 +479,11 @@ func TestCVESearch(t *testing.T) {
So(err, ShouldBeNil)
So(len(imgFixedCVEResult.ImgResults.ImgResultForFixedCVE.Tags), ShouldEqual, 0)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-test\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-test\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"b/zot-squashfs-test:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"b/zot-squashfs-test:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -491,108 +492,108 @@ func TestCVESearch(t *testing.T) {
So(err, ShouldBeNil)
So(len(cveSquashFSResult.ImgList.CVEResultForImage.CVEList), ShouldBeZeroValue)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-squashfs-noindex:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-noindex:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-noindex\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-noindex\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-squashfs-invalid-index:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-invalid-index:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-invalid-index\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-invalid-index\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-squashfs-noblobs:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-noblobs:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-noblob\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-noblob\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-test\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-test\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-squashfs-invalid-blob:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-invalid-blob:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-invalid-blob\"){Tags{Name%20Timestamp}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cvid + "\",image:\"zot-squashfs-invalid-blob\"){Tags{Name%20Timestamp}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-squashfs-test\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-test\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"cntos\"){Tag%20CVEList{Id%20Description%20Severity}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"cntos\"){Tag%20CVEList{Id%20Description%20Severity}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListForCVE(id:\"CVE-201-20482\"){Name%20Tags}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListForCVE(id:\"CVE-201-20482\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test\"){Tag%20CVEList{Id%20Description}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test\"){Tag%20CVEList{Id%20Description}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){Tag}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){Tag}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Description%20Severity}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Description%20Severity}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Description%20Severity}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Description%20Severity}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Severity}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Severity}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Description}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Description}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Description}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Description}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
// Testing Invalid Search URL
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(image:\"zot-test:0.0.1\"){Ta%20CVEList{Id%20Description%20Severity}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){Ta%20CVEList{Id%20Description%20Severity}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListForCVE(tet:\"CVE-2018-20482\"){Name%20Tags}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListForCVE(tet:\"CVE-2018-20482\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageistForCVE(id:\"CVE-2018-20482\"){Name%20Tags}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageistForCVE(id:\"CVE-2018-20482\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListForCVE(id:\"CVE-2018-20482\"){ame%20Tags}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListForCVE(id:\"CVE-2018-20482\"){ame%20Tags}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={CVEListForImage(reo:\"zot-test:1.0.0\"){Tag%20CVEList{Id%20Description%20Severity}}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={CVEListForImage(reo:\"zot-test:1.0.0\"){Tag%20CVEList{Id%20Description%20Severity}}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/query?query={ImageListForCVE(id:\"" + cvid + "\"){Name%20Tags}}")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.ExtSearchPrefix + "?query={ImageListForCVE(id:\"" + cvid + "\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
})
@ -648,11 +649,11 @@ func TestCVEConfig(t *testing.T) {
time.Sleep(100 * time.Millisecond)
}
resp, _ := resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/v2/")
resp, _ := resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.RoutePrefix + "/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + "/v2/_catalog")
resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -721,7 +722,7 @@ func TestHTTPOptionsResponse(t *testing.T) {
time.Sleep(100 * time.Millisecond)
}
resp, _ := resty.R().Options(baseURL + "/v2/_catalog")
resp, _ := resty.R().Options(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)

View file

@ -16,6 +16,7 @@ import (
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring"
digestinfo "zotregistry.io/zot/pkg/extensions/search/digest"
@ -179,13 +180,14 @@ func TestDigestSearchHTTP(t *testing.T) {
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(baseURL + "/query")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
// "sha" should match all digests in all images
resp, err = resty.R().Get(baseURL + "/query?query={ImageListForDigest(id:\"sha\"){Name%20Tags}}")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix +
"?query={ImageListForDigest(id:\"sha\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -200,7 +202,8 @@ func TestDigestSearchHTTP(t *testing.T) {
// Call should return {"data":{"ImageListForDigest":[{"Name":"zot-test","Tags":["0.0.1"]}]}}
// "2bacca16" should match the manifest of 1 image
resp, err = resty.R().Get(baseURL + "/query?query={ImageListForDigest(id:\"2bacca16\"){Name%20Tags}}")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix +
"?query={ImageListForDigest(id:\"2bacca16\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -215,7 +218,8 @@ func TestDigestSearchHTTP(t *testing.T) {
// Call should return {"data":{"ImageListForDigest":[{"Name":"zot-test","Tags":["0.0.1"]}]}}
// "adf3bb6c" should match the config of 1 image
resp, err = resty.R().Get(baseURL + "/query?query={ImageListForDigest(id:\"adf3bb6c\"){Name%20Tags}}")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix +
"?query={ImageListForDigest(id:\"adf3bb6c\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -230,7 +234,8 @@ func TestDigestSearchHTTP(t *testing.T) {
// Call should return {"data":{"ImageListForDigest":[{"Name":"zot-cve-test","Tags":["0.0.1"]}]}}
// "7a0437f0" should match the layer of 1 image
resp, err = resty.R().Get(baseURL + "/query?query={ImageListForDigest(id:\"7a0437f0\"){Name%20Tags}}")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix +
"?query={ImageListForDigest(id:\"7a0437f0\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -245,7 +250,8 @@ func TestDigestSearchHTTP(t *testing.T) {
// Call should return {"data":{"ImageListForDigest":[]}}
// "1111111" should match 0 images
resp, err = resty.R().Get(baseURL + "/query?query={ImageListForDigest(id:\"1111111\"){Name%20Tags}}")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix +
"?query={ImageListForDigest(id:\"1111111\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -256,7 +262,8 @@ func TestDigestSearchHTTP(t *testing.T) {
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 0)
// Call should return {"errors": [{....}]", data":null}}
resp, err = resty.R().Get(baseURL + "/query?query={ImageListForDigest(id:\"1111111\"){Name%20Tag343s}}")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix +
"?query={ImageListForDigest(id:\"1111111\"){Name%20Tag343s}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
@ -321,12 +328,13 @@ func TestDigestSearchHTTPSubPaths(t *testing.T) {
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(baseURL + "/query")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(baseURL + "/query?query={ImageListForDigest(id:\"sha\"){Name%20Tags}}")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix +
"?query={ImageListForDigest(id:\"sha\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
@ -380,7 +388,7 @@ func TestDigestSearchDisabled(t *testing.T) {
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(baseURL + "/query")
resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix)
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 404)

View file

@ -21,6 +21,7 @@ import (
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"gopkg.in/resty.v1"
zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/test"
@ -77,7 +78,7 @@ type Tags struct {
func getUpstreamCatalog(client *resty.Client, upstreamURL string, log log.Logger) (catalog, error) {
var catalog catalog
registryCatalogURL := fmt.Sprintf("%s%s", upstreamURL, "/v2/_catalog")
registryCatalogURL := fmt.Sprintf("%s%s%s", upstreamURL, constants.RoutePrefix, constants.ExtCatalogPrefix)
resp, err := client.R().SetHeader("Content-Type", "application/json").Get(registryCatalogURL)
if err != nil {

View file

@ -37,6 +37,7 @@ import (
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/cli"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/sync"
@ -1315,11 +1316,11 @@ func TestNoImagesByRegex(t *testing.T) {
dctlr.Shutdown()
}()
resp, err := destClient.R().Get(destBaseURL + "/v2/" + testImage + "/manifests/" + testImageTag)
resp, err := destClient.R().Get(destBaseURL + constants.RoutePrefix + testImage + "/manifests/" + testImageTag)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 404)
resp, err = destClient.R().Get(destBaseURL + "/v2/_catalog")
resp, err = destClient.R().Get(destBaseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
So(err, ShouldBeNil)
So(resp, ShouldNotBeEmpty)
So(resp.StatusCode(), ShouldEqual, 200)
@ -1743,7 +1744,7 @@ func TestSubPaths(t *testing.T) {
var destTagsList TagsList
for {
resp, err := resty.R().Get(destBaseURL + "/v2" + path.Join(subpath, testImage) + "/tags/list")
resp, err := resty.R().Get(destBaseURL + constants.RoutePrefix + path.Join(subpath, testImage) + "/tags/list")
if err != nil {
panic(err)
}
@ -2169,7 +2170,7 @@ func TestPeriodicallySignaturesErr(t *testing.T) {
cosignTag := string(imageManifestDigest.Algorithm()) + "-" + imageManifestDigest.Hex() +
"." + remote.SignatureTagSuffix
getCosignManifestURL := srcBaseURL + path.Join("/v2", repoName, "manifests", cosignTag)
getCosignManifestURL := srcBaseURL + path.Join(constants.RoutePrefix, repoName, "manifests", cosignTag)
mResp, err := resty.R().Get(getCosignManifestURL)
So(err, ShouldBeNil)
@ -2444,7 +2445,7 @@ func TestSignatures(t *testing.T) {
// test cosign signatures errors
// based on manifest digest get cosign manifest
cosignEncodedDigest := strings.Replace(digest.String(), ":", "-", 1) + ".sig"
getCosignManifestURL := srcBaseURL + path.Join("/v2", repoName, "manifests", cosignEncodedDigest)
getCosignManifestURL := srcBaseURL + path.Join(constants.RoutePrefix, repoName, "manifests", cosignEncodedDigest)
mResp, err := resty.R().Get(getCosignManifestURL)
So(err, ShouldBeNil)
@ -3057,7 +3058,7 @@ func TestSignaturesOnDemand(t *testing.T) {
// test negative case
cosignEncodedDigest := strings.Replace(digest.String(), ":", "-", 1) + ".sig"
getCosignManifestURL := srcBaseURL + path.Join("/v2", repoName, "manifests", cosignEncodedDigest)
getCosignManifestURL := srcBaseURL + path.Join(constants.RoutePrefix, repoName, "manifests", cosignEncodedDigest)
mResp, err := resty.R().Get(getCosignManifestURL)
So(err, ShouldBeNil)

View file

@ -1,12 +1,13 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at
// 2019-12-11 12:03:05.055900322 -0800 PST m=+0.052058015
// 2022-05-20 22:28:21.990630149 +0000 UTC m=+0.151110280
package swagger
import (
"bytes"
"encoding/json"
"strings"
"github.com/alecthomas/template"
"github.com/swaggo/swag"
@ -16,8 +17,8 @@ var doc = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "APIs for Open Container Initiative Distribution Specification",
"title": "Open Container Initiative Distribution Specification",
"description": "{{.Description}}",
"title": "{{.Title}}",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
@ -27,11 +28,66 @@ var doc = `{
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "v0.1.0-dev"
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/oras/artifacts/v1/{name": {
"get": {
"description": "Get references for an image given a digest and artifact type",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get references for an image",
"parameters": [
{
"type": "string",
"description": "repository name",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"description": "image digest",
"name": "digest",
"in": "path",
"required": true
},
{
"type": "string",
"description": "artifact type",
"name": "artifactType",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"404": {
"description": "not found",
"schema": {
"type": "string"
}
},
"500": {
"description": "internal server error",
"schema": {
"type": "string"
}
}
}
}
},
"/v2/": {
"get": {
"description": "Check if this API version is supported",
@ -44,7 +100,7 @@ var doc = `{
"summary": "Check API support",
"responses": {
"200": {
"description": "ok",
"description": "ok\".",
"schema": {
"type": "string"
}
@ -66,7 +122,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.RepositoryList"
}
},
@ -79,6 +134,26 @@ var doc = `{
}
}
},
"/v2/_oci/ext/discover": {
"get": {
"description": "List all extensions present on registry",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "List Registry level extensions",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.ExtensionList"
}
}
}
}
},
"/v2/{name}/blobs/uploads": {
"post": {
"description": "Create a new image blob/layer upload",
@ -107,7 +182,7 @@ var doc = `{
"headers": {
"Location": {
"type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}"
"description": "/v2/{name}/blobs/uploads/{session_id}"
},
"Range": {
"type": "string",
@ -130,9 +205,9 @@ var doc = `{
}
}
},
"/v2/{name}/blobs/uploads/{uuid}": {
"/v2/{name}/blobs/uploads/{session_id}": {
"get": {
"description": "Get an image's blob/layer upload given a uuid",
"description": "Get an image's blob/layer upload given a session_id",
"consumes": [
"application/json"
],
@ -150,8 +225,8 @@ var doc = `{
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
}
@ -196,8 +271,8 @@ var doc = `{
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
},
@ -249,8 +324,8 @@ var doc = `{
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
}
@ -277,7 +352,7 @@ var doc = `{
}
},
"patch": {
"description": "Resume an image's blob/layer upload given an uuid",
"description": "Resume an image's blob/layer upload given an session_id",
"consumes": [
"application/json"
],
@ -295,8 +370,8 @@ var doc = `{
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
}
@ -310,7 +385,7 @@ var doc = `{
"headers": {
"Location": {
"type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}"
"description": "/v2/{name}/blobs/uploads/{session_id}"
},
"Range": {
"type": "string",
@ -375,7 +450,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest"
}
}
@ -444,11 +518,10 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest"
},
"headers": {
"api.DistContentDigestKey": {
"constants.DistContentDigestKey": {
"type": "object",
"description": "OK"
}
@ -487,11 +560,10 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest"
},
"headers": {
"api.DistContentDigestKey": {
"constants.DistContentDigestKey": {
"type": "object",
"description": "OK"
}
@ -629,7 +701,7 @@ var doc = `{
"type": "string"
},
"headers": {
"api.DistContentDigestKey": {
"cosntants.DistContentDigestKey": {
"type": "object",
"description": "OK"
}
@ -642,7 +714,7 @@ var doc = `{
}
},
"500": {
"description": "internal server error",
"description": "internal server error\".",
"schema": {
"type": "string"
}
@ -667,16 +739,35 @@ var doc = `{
"name": "name",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "limit entries for pagination",
"name": "n",
"in": "query",
"required": true
},
{
"type": "string",
"description": "last tag value for pagination",
"name": "last",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageTags"
}
},
"400": {
"description": "bad request\".",
"schema": {
"type": "string"
}
},
"404": {
"description": "not found",
"schema": {
@ -688,6 +779,9 @@ var doc = `{
}
},
"definitions": {
"api.ExtensionList": {
"type": "object"
},
"api.ImageManifest": {
"type": "object"
},
@ -729,7 +823,14 @@ type swaggerInfo struct {
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{Schemes: []string{}}
var SwaggerInfo = swaggerInfo{
Version: "v0.1.0-dev",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "Open Container Initiative Distribution Specification",
Description: "APIs for Open Container Initiative Distribution Specification",
}
type s struct{}
@ -737,7 +838,11 @@ func New() *s {
return &s{}
}
func (s *s) ReadDoc() string {
sInfo := SwaggerInfo
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string {
a, _ := json.Marshal(v)
@ -749,7 +854,7 @@ func (s *s) ReadDoc() string {
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, SwaggerInfo); err != nil {
if err := t.Execute(&tpl, sInfo); err != nil {
return doc
}

View file

@ -14,9 +14,62 @@
},
"version": "v0.1.0-dev"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/oras/artifacts/v1/{name": {
"get": {
"description": "Get references for an image given a digest and artifact type",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get references for an image",
"parameters": [
{
"type": "string",
"description": "repository name",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"description": "image digest",
"name": "digest",
"in": "path",
"required": true
},
{
"type": "string",
"description": "artifact type",
"name": "artifactType",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"404": {
"description": "not found",
"schema": {
"type": "string"
}
},
"500": {
"description": "internal server error",
"schema": {
"type": "string"
}
}
}
}
},
"/v2/": {
"get": {
"description": "Check if this API version is supported",
@ -29,7 +82,7 @@
"summary": "Check API support",
"responses": {
"200": {
"description": "ok",
"description": "ok\".",
"schema": {
"type": "string"
}
@ -51,7 +104,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.RepositoryList"
}
},
@ -64,6 +116,26 @@
}
}
},
"/v2/_oci/ext/discover": {
"get": {
"description": "List all extensions present on registry",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "List Registry level extensions",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.ExtensionList"
}
}
}
}
},
"/v2/{name}/blobs/uploads": {
"post": {
"description": "Create a new image blob/layer upload",
@ -92,7 +164,7 @@
"headers": {
"Location": {
"type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}"
"description": "/v2/{name}/blobs/uploads/{session_id}"
},
"Range": {
"type": "string",
@ -115,9 +187,9 @@
}
}
},
"/v2/{name}/blobs/uploads/{uuid}": {
"/v2/{name}/blobs/uploads/{session_id}": {
"get": {
"description": "Get an image's blob/layer upload given a uuid",
"description": "Get an image's blob/layer upload given a session_id",
"consumes": [
"application/json"
],
@ -135,8 +207,8 @@
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
}
@ -181,8 +253,8 @@
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
},
@ -234,8 +306,8 @@
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
}
@ -262,7 +334,7 @@
}
},
"patch": {
"description": "Resume an image's blob/layer upload given an uuid",
"description": "Resume an image's blob/layer upload given an session_id",
"consumes": [
"application/json"
],
@ -280,8 +352,8 @@
},
{
"type": "string",
"description": "upload uuid",
"name": "uuid",
"description": "upload session_id",
"name": "session_id",
"in": "path",
"required": true
}
@ -295,7 +367,7 @@
"headers": {
"Location": {
"type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}"
"description": "/v2/{name}/blobs/uploads/{session_id}"
},
"Range": {
"type": "string",
@ -360,7 +432,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest"
}
}
@ -429,11 +500,10 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest"
},
"headers": {
"api.DistContentDigestKey": {
"constants.DistContentDigestKey": {
"type": "object",
"description": "OK"
}
@ -472,11 +542,10 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest"
},
"headers": {
"api.DistContentDigestKey": {
"constants.DistContentDigestKey": {
"type": "object",
"description": "OK"
}
@ -614,7 +683,7 @@
"type": "string"
},
"headers": {
"api.DistContentDigestKey": {
"cosntants.DistContentDigestKey": {
"type": "object",
"description": "OK"
}
@ -627,7 +696,7 @@
}
},
"500": {
"description": "internal server error",
"description": "internal server error\".",
"schema": {
"type": "string"
}
@ -652,16 +721,35 @@
"name": "name",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "limit entries for pagination",
"name": "n",
"in": "query",
"required": true
},
{
"type": "string",
"description": "last tag value for pagination",
"name": "last",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ImageTags"
}
},
"400": {
"description": "bad request\".",
"schema": {
"type": "string"
}
},
"404": {
"description": "not found",
"schema": {
@ -673,6 +761,9 @@
}
},
"definitions": {
"api.ExtensionList": {
"type": "object"
},
"api.ImageManifest": {
"type": "object"
},

View file

@ -1,5 +1,6 @@
basePath: '{{.BasePath}}'
definitions:
api.ExtensionList:
type: object
api.ImageManifest:
type: object
api.ImageTags:
@ -18,7 +19,6 @@ definitions:
type: string
type: array
type: object
host: '{{.Host}}'
info:
contact:
email: support@swagger.io
@ -31,6 +31,43 @@ info:
title: Open Container Initiative Distribution Specification
version: v0.1.0-dev
paths:
/oras/artifacts/v1/{name:
get:
consumes:
- application/json
description: Get references for an image given a digest and artifact type
parameters:
- description: repository name
in: path
name: name
required: true
type: string
- description: image digest
in: path
name: digest
required: true
type: string
- description: artifact type
in: query
name: artifactType
required: true
type: string
produces:
- application/json
responses:
"200":
description: ok
schema:
type: string
"404":
description: not found
schema:
type: string
"500":
description: internal server error
schema:
type: string
summary: Get references for an image
/v2/:
get:
consumes:
@ -40,7 +77,7 @@ paths:
- application/json
responses:
"200":
description: ok
description: ok".
schema:
type: string
summary: Check API support
@ -56,12 +93,24 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.RepositoryList'
type: object
"500":
description: internal server error
schema:
type: string
summary: List image repositories
/v2/_oci/ext/discover:
get:
consumes:
- application/json
description: List all extensions present on registry
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.ExtensionList'
summary: List Registry level extensions
/v2/{name}/blobs/{digest}:
delete:
consumes:
@ -108,7 +157,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.ImageManifest'
type: object
summary: Get image blob/layer
head:
consumes:
@ -131,12 +179,11 @@ paths:
"200":
description: OK
headers:
api.DistContentDigestKey:
constants.DistContentDigestKey:
description: OK
type: object
schema:
$ref: '#/definitions/api.ImageManifest'
type: object
summary: Check image blob/layer
/v2/{name}/blobs/uploads:
post:
@ -156,7 +203,7 @@ paths:
description: accepted
headers:
Location:
description: /v2/{name}/blobs/uploads/{uuid}
description: /v2/{name}/blobs/uploads/{session_id}
type: string
Range:
description: bytes=0-0
@ -172,7 +219,7 @@ paths:
schema:
type: string
summary: Create image blob/layer upload
/v2/{name}/blobs/uploads/{uuid}:
/v2/{name}/blobs/uploads/{session_id}:
delete:
consumes:
- application/json
@ -183,9 +230,9 @@ paths:
name: name
required: true
type: string
- description: upload uuid
- description: upload session_id
in: path
name: uuid
name: session_id
required: true
type: string
produces:
@ -207,16 +254,16 @@ paths:
get:
consumes:
- application/json
description: Get an image's blob/layer upload given a uuid
description: Get an image's blob/layer upload given a session_id
parameters:
- description: repository name
in: path
name: name
required: true
type: string
- description: upload uuid
- description: upload session_id
in: path
name: uuid
name: session_id
required: true
type: string
produces:
@ -238,16 +285,16 @@ paths:
patch:
consumes:
- application/json
description: Resume an image's blob/layer upload given an uuid
description: Resume an image's blob/layer upload given an session_id
parameters:
- description: repository name
in: path
name: name
required: true
type: string
- description: upload uuid
- description: upload session_id
in: path
name: uuid
name: session_id
required: true
type: string
produces:
@ -257,7 +304,7 @@ paths:
description: accepted
headers:
Location:
description: /v2/{name}/blobs/uploads/{uuid}
description: /v2/{name}/blobs/uploads/{session_id}
type: string
Range:
description: bytes=0-128
@ -291,9 +338,9 @@ paths:
name: name
required: true
type: string
- description: upload uuid
- description: upload session_id
in: path
name: uuid
name: session_id
required: true
type: string
- description: blob/layer digest
@ -362,12 +409,11 @@ paths:
"200":
description: OK
headers:
api.DistContentDigestKey:
constants.DistContentDigestKey:
description: OK
type: object
schema:
$ref: '#/definitions/api.ImageManifest'
type: object
"404":
description: not found
schema:
@ -398,7 +444,7 @@ paths:
"200":
description: ok
headers:
api.DistContentDigestKey:
cosntants.DistContentDigestKey:
description: OK
type: object
schema:
@ -408,7 +454,7 @@ paths:
schema:
type: string
"500":
description: internal server error
description: internal server error".
schema:
type: string
summary: Check image manifest
@ -458,6 +504,16 @@ paths:
name: name
required: true
type: string
- description: limit entries for pagination
in: query
name: "n"
required: true
type: integer
- description: last tag value for pagination
in: query
name: last
required: true
type: string
produces:
- application/json
responses:
@ -465,7 +521,10 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.ImageTags'
type: object
"400":
description: bad request".
schema:
type: string
"404":
description: not found
schema: