0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-01-13 22:50:38 -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 - name: Install other dependencies
run: | run: |
cd $GITHUB_WORKSPACE 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 update
sudo apt-get install rpm sudo apt-get install rpm
sudo apt-get install snapd 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 ./... $(GOLINTER) --config ./golangcilint.yaml run --enable-all --out-format=colored-line-number --build-tags stress,extended,containers_image_openpgp ./...
swagger/docs.go: 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 swag init -o swagger -g pkg/api/routes.go
.PHONY: swagger .PHONY: swagger
swagger: swagger/docs.go swagger: swagger/docs.go pkg/api/routes.go
.PHONY: update-licenses .PHONY: update-licenses
update-licenses: update-licenses:

View file

@ -20,6 +20,7 @@ import (
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
godigest "github.com/opencontainers/go-digest" godigest "github.com/opencontainers/go-digest"
"gopkg.in/resty.v1" "gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api/constants"
) )
const ( const (
@ -323,7 +324,7 @@ func GetCatalog(workdir, url, auth, repo string, requests int, config testConfig
}() }()
// send request and get response // 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) latency = time.Since(start)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

17
go.mod
View file

@ -47,7 +47,6 @@ require (
github.com/spf13/cobra v1.4.0 github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.11.0 github.com/spf13/viper v1.11.0
github.com/stretchr/testify v1.7.1 github.com/stretchr/testify v1.7.1
github.com/swaggo/http-swagger v1.2.6
github.com/swaggo/swag v1.8.1 github.com/swaggo/swag v1.8.1
github.com/urfave/cli/v2 v2.4.0 github.com/urfave/cli/v2 v2.4.0
github.com/vektah/gqlparser/v2 v2.4.1 github.com/vektah/gqlparser/v2 v2.4.1
@ -57,9 +56,11 @@ require (
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )
require github.com/open-policy-agent/opa v0.37.0 // indirect
require ( require (
github.com/open-policy-agent/opa v0.37.0 // indirect github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220217185014-dd38b7ed8a99
github.com/opencontainers/distribution-spec v1.0.1 github.com/swaggo/http-swagger v1.2.8
) )
require ( require (
@ -90,8 +91,6 @@ require (
github.com/PaesslerAG/gval v1.0.0 // indirect github.com/PaesslerAG/gval v1.0.0 // indirect
github.com/PaesslerAG/jsonpath v0.1.1 // indirect github.com/PaesslerAG/jsonpath v0.1.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // 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/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // 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/analysis v0.21.2 // indirect
github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/errors v0.20.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // 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/loads v0.21.1 // indirect
github.com/go-openapi/runtime v0.23.3 // 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/strfmt v0.21.2 // indirect
github.com/go-openapi/swag v0.21.1 // indirect github.com/go-openapi/swag v0.21.1 // indirect
github.com/go-openapi/validate v0.21.0 // 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/multierr v1.7.0 // indirect
go.uber.org/zap v1.21.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/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/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // 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/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // 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/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.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.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/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-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/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.10.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM= 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.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.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.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.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.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.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.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.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.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.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.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.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.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= 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.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 h1:OUXB+RAcxQpmXeNW2BN1wYzQQvVCPF1T9zv+QXGr9Wg=
github.com/open-policy-agent/opa v0.37.0/go.mod h1:xX3NUCZuXK8f0CNhFQvhm4495mZLptf94pIkWRLaFqo= 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/specs-go v0.0.0-20220217185014-dd38b7ed8a99 h1:yGqpYh1h3ZXxGg4Mqd5ZwhlwQ9wXOgvuodxbxJA7iMY=
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/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-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 v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/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/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 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= 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.8 h1:TVjxLU7qoqofJ9qynJazmpTGs/p4Kx9FTp7YYwOkJb0=
github.com/swaggo/http-swagger v1.2.6/go.mod h1:CcoICgY3yVDk2u1LQUCMHbAj0fjlxIX+873psXlIKNA= github.com/swaggo/http-swagger v1.2.8/go.mod h1:FrQwV7rx+A5t11PIX8d+tFJa2GKx11RdAXQptllPQHg=
github.com/swaggo/swag v1.7.9/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU=
github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI= 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/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= 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-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-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-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-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-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-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/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-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-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-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-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-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-20201210144234-2321bbc49cbf/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 ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -10,6 +11,7 @@ import (
glob "github.com/bmatcuk/doublestar/v4" glob "github.com/bmatcuk/doublestar/v4"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/common" "zotregistry.io/zot/pkg/common"
"zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/log"
) )
@ -191,7 +193,7 @@ func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
ctx := acCtrlr.getContext(username, request) ctx := acCtrlr.getContext(username, request)
// will return only repos on which client is authorized to read // 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)) next.ServeHTTP(response, request.WithContext(ctx))
return return

View file

@ -1,11 +1,12 @@
package constants package constants
const ( const (
ArtifactSpecRoutePrefix = "/oras/artifacts/v1" ArtifactSpecRoutePrefix = "/oras/artifacts/v1"
RoutePrefix = "/v2" RoutePrefix = "/v2"
DistAPIVersion = "Docker-Distribution-API-Version" DistAPIVersion = "Docker-Distribution-API-Version"
DistContentDigestKey = "Docker-Content-Digest" DistContentDigestKey = "Docker-Content-Digest"
BlobUploadUUID = "Blob-Upload-UUID" BlobUploadUUID = "Blob-Upload-UUID"
DefaultMediaType = "application/json" DefaultMediaType = "application/json"
BinaryMediaType = "application/octet-stream" 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" "github.com/mitchellh/mapstructure"
vldap "github.com/nmcclain/ldap" vldap "github.com/nmcclain/ldap"
notreg "github.com/notaryproject/notation/pkg/registry" notreg "github.com/notaryproject/notation/pkg/registry"
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
godigest "github.com/opencontainers/go-digest" godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1" ispec "github.com/opencontainers/image-spec/specs-go/v1"
artifactspec "github.com/oras-project/artifacts-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"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants" "zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/storage" "zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/test" "zotregistry.io/zot/pkg/test"
) )
@ -1878,7 +1880,7 @@ func TestAuthorizationWithBasicAuth(t *testing.T) {
// everybody should have access to /v2/_catalog // everybody should have access to /v2/_catalog
resp, err = resty.R().SetBasicAuth(username, passphrase). resp, err = resty.R().SetBasicAuth(username, passphrase).
Get(baseURL + "/v2/_catalog") Get(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(resp, ShouldNotBeNil) So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK) 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") assert.Equal(t, tagResponse.StatusCode(), http.StatusOK, "response status code should return success code")
repoResponse, err := client.R().SetBasicAuth(username, passphrase). 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, err, nil, "Error should be nil")
assert.Equal(t, repoResponse.StatusCode(), http.StatusOK, "response status code should return success code") 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 { func getAllBlobs(imagePath string) []string {
blobList := make([]string, 0) blobList := make([]string, 0)

View file

@ -25,6 +25,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
notreg "github.com/notaryproject/notation/pkg/registry" 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" ispec "github.com/opencontainers/image-spec/specs-go/v1"
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1" artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
httpSwagger "github.com/swaggo/http-swagger" httpSwagger "github.com/swaggo/http-swagger"
@ -69,6 +70,7 @@ func (rh *RouteHandler) SetupRoutes() {
rh.c.Router.Use(AuthzHandler(rh.c)) 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 := rh.c.Router.PathPrefix(constants.RoutePrefix).Subrouter()
{ {
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", NameRegexp.String()), prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", NameRegexp.String()),
@ -97,8 +99,10 @@ func (rh *RouteHandler) SetupRoutes() {
rh.UpdateBlobUpload).Methods("PUT") rh.UpdateBlobUpload).Methods("PUT")
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()), prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()),
rh.DeleteBlobUpload).Methods("DELETE") rh.DeleteBlobUpload).Methods("DELETE")
prefixedRouter.HandleFunc("/_catalog", prefixedRouter.HandleFunc(constants.ExtCatalogPrefix,
rh.ListRepositories).Methods(allowedMethods("GET")...) rh.ListRepositories).Methods(allowedMethods("GET")...)
prefixedRouter.HandleFunc(constants.ExtOciDiscoverPrefix,
rh.ListExtensions).Methods(allowedMethods("GET")...)
prefixedRouter.HandleFunc("/", prefixedRouter.HandleFunc("/",
rh.CheckVersionSupport).Methods(allowedMethods("GET")...) rh.CheckVersionSupport).Methods(allowedMethods("GET")...)
} }
@ -336,6 +340,10 @@ type ImageManifest struct {
ispec.Manifest ispec.Manifest
} }
type ExtensionList struct {
extensions.ExtensionList
}
// GetManifest godoc // GetManifest godoc
// @Summary Get image manifest // @Summary Get image manifest
// @Description Get an image's manifest given a reference or a digest // @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) 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) { func (rh *RouteHandler) GetMetrics(w http.ResponseWriter, r *http.Request) {
m := rh.c.Metrics.ReceiveMetrics() m := rh.c.Metrics.ReceiveMetrics()
WriteJSON(w, http.StatusOK, m) WriteJSON(w, http.StatusOK, m)

View file

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

View file

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

View file

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

View file

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

View file

@ -16,6 +16,7 @@ import (
"zotregistry.io/zot/errors" "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config" extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring" "zotregistry.io/zot/pkg/extensions/monitoring"
"zotregistry.io/zot/pkg/storage" "zotregistry.io/zot/pkg/storage"
@ -332,7 +333,7 @@ func applyDefaultValues(config *config.Config, viperInstance *viper.Viper) {
} }
if config.Extensions.Metrics.Prometheus == nil { 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" "github.com/olekukonko/tablewriter"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
zotErrors "zotregistry.io/zot/errors" zotErrors "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api/constants"
) )
type SearchService interface { type SearchService interface {
@ -70,7 +71,8 @@ func (service searchService) getAllImages(ctx context.Context, config searchConf
catalog := &catalogResponse{} 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 err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return return
@ -453,7 +455,7 @@ func (service searchService) makeGraphQLQuery(ctx context.Context, config search
username, password, query string, username, password, query string,
resultPtr interface{}, resultPtr interface{},
) error { ) error {
endPoint, err := combineServerAndEndpointURL(*config.servURL, "/query") endPoint, err := combineServerAndEndpointURL(*config.servURL, constants.ExtSearchPrefix)
if err != nil { if err != nil {
return err 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("Make API calls to the controller", t, func(c C) {
Convey("Check version", func() { Convey("Check version", func() {
_, _ = Print("\nCheck version") _, _ = Print("\nCheck version")
resp, err := resty.R().Get(baseURL + "/v2/") resp, err := resty.R().Get(baseURL + constants.RoutePrefix + "/")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK) So(resp.StatusCode(), ShouldEqual, http.StatusOK)
}) })
Convey("Get repository catalog", func() { Convey("Get repository catalog", func() {
_, _ = Print("\nGet repository catalog") _, _ = 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(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK) So(resp.StatusCode(), ShouldEqual, http.StatusOK)
So(resp.String(), ShouldNotBeEmpty) So(resp.String(), ShouldNotBeEmpty)
@ -77,7 +77,8 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted) 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(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK) So(resp.StatusCode(), ShouldEqual, http.StatusOK)
So(resp.String(), ShouldNotBeEmpty) 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 ( import (
"context" "context"
"fmt"
goSync "sync" goSync "sync"
"time" "time"
gqlHandler "github.com/99designs/gqlgen/graphql/handler" gqlHandler "github.com/99designs/gqlgen/graphql/handler"
"github.com/gorilla/mux" "github.com/gorilla/mux"
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/extensions/scrub" "zotregistry.io/zot/pkg/extensions/scrub"
"zotregistry.io/zot/pkg/extensions/search" "zotregistry.io/zot/pkg/extensions/search"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve" 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.Enable &&
config.Extensions.Metrics.Prometheus != nil { config.Extensions.Metrics.Prometheus != nil {
if config.Extensions.Metrics.Prometheus.Path == "" { 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 { } else {
log.Info().Msg("Metrics config not provided, skipping Metrics config update") 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 ... // SetupRoutes ...
func SetupRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController, func SetupRoutes(config *config.Config, router *mux.Router, storeController storage.StoreController, l log.Logger,
l log.Logger,
) { ) {
// fork a new zerolog child to avoid data race // fork a new zerolog child to avoid data race
log := log.Logger{Logger: l.With().Caller().Timestamp().Logger()} 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) 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))) Handler(gqlHandler.NewDefaultServer(search.NewExecutableSchema(resConfig)))
} }

View file

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage" "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") "any extensions, please build zot full binary for this feature")
} }
// GetExtensions...
func GetExtensions(config *config.Config) distext.ExtensionList {
return distext.ExtensionList{}
}
// EnableSyncExtension ... // EnableSyncExtension ...
func EnableSyncExtension(ctx context.Context, config *config.Config, wg *goSync.WaitGroup, func EnableSyncExtension(ctx context.Context, config *config.Config, wg *goSync.WaitGroup,
storeController storage.StoreController, log log.Logger, storeController storage.StoreController, log log.Logger,
@ -42,7 +48,8 @@ func EnableScrubExtension(config *config.Config, storeController storage.StoreCo
} }
// SetupRoutes ... // 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 " + 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") "any extensions, please build zot full binary for this feature")
} }

View file

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

View file

@ -20,6 +20,7 @@ import (
"gopkg.in/resty.v1" "gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
extconf "zotregistry.io/zot/pkg/extensions/config" extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring" "zotregistry.io/zot/pkg/extensions/monitoring"
"zotregistry.io/zot/pkg/extensions/search/common" "zotregistry.io/zot/pkg/extensions/search/common"
@ -430,7 +431,7 @@ func TestCVESearch(t *testing.T) {
err = json.Unmarshal(resp.Body(), &apiErr) err = json.Unmarshal(resp.Body(), &apiErr)
So(err, ShouldBeNil) So(err, ShouldBeNil)
resp, err = resty.R().Get(baseURL + "/query/") resp, err = resty.R().Get(baseURL + constants.ExtSearchPrefix)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(resp, ShouldNotBeNil) So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 401) So(resp.StatusCode(), ShouldEqual, 401)
@ -446,11 +447,11 @@ func TestCVESearch(t *testing.T) {
So(resp, ShouldNotBeNil) So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) So(resp.StatusCode(), ShouldEqual, 200)
@ -461,7 +462,7 @@ func TestCVESearch(t *testing.T) {
cvid := cveResult.ImgList.CVEResultForImage.CVEList[0].ID 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) So(resp.StatusCode(), ShouldEqual, 200)
@ -470,7 +471,7 @@ func TestCVESearch(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(imgFixedCVEResult.ImgResults.ImgResultForFixedCVE.Tags), ShouldEqual, 0) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) So(resp.StatusCode(), ShouldEqual, 200)
@ -478,11 +479,11 @@ func TestCVESearch(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(imgFixedCVEResult.ImgResults.ImgResultForFixedCVE.Tags), ShouldEqual, 0) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) So(resp.StatusCode(), ShouldEqual, 200)
@ -491,108 +492,108 @@ func TestCVESearch(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveSquashFSResult.ImgList.CVEResultForImage.CVEList), ShouldBeZeroValue) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) So(resp.StatusCode(), ShouldEqual, 200)
// Testing Invalid Search URL // 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 422) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) So(resp.StatusCode(), ShouldEqual, 200)
}) })
@ -648,11 +649,11 @@ func TestCVEConfig(t *testing.T) {
time.Sleep(100 * time.Millisecond) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, 200) So(resp.StatusCode(), ShouldEqual, 200)
@ -721,7 +722,7 @@ func TestHTTPOptionsResponse(t *testing.T) {
time.Sleep(100 * time.Millisecond) 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, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent) So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)

View file

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

View file

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

View file

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

View file

@ -1,12 +1,13 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at // 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 package swagger
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"strings"
"github.com/alecthomas/template" "github.com/alecthomas/template"
"github.com/swaggo/swag" "github.com/swaggo/swag"
@ -16,8 +17,8 @@ var doc = `{
"schemes": {{ marshal .Schemes }}, "schemes": {{ marshal .Schemes }},
"swagger": "2.0", "swagger": "2.0",
"info": { "info": {
"description": "APIs for Open Container Initiative Distribution Specification", "description": "{{.Description}}",
"title": "Open Container Initiative Distribution Specification", "title": "{{.Title}}",
"contact": { "contact": {
"name": "API Support", "name": "API Support",
"url": "http://www.swagger.io/support", "url": "http://www.swagger.io/support",
@ -27,11 +28,66 @@ var doc = `{
"name": "Apache 2.0", "name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html" "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}, },
"version": "v0.1.0-dev" "version": "{{.Version}}"
}, },
"host": "{{.Host}}", "host": "{{.Host}}",
"basePath": "{{.BasePath}}", "basePath": "{{.BasePath}}",
"paths": { "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/": { "/v2/": {
"get": { "get": {
"description": "Check if this API version is supported", "description": "Check if this API version is supported",
@ -44,7 +100,7 @@ var doc = `{
"summary": "Check API support", "summary": "Check API support",
"responses": { "responses": {
"200": { "200": {
"description": "ok", "description": "ok\".",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@ -66,7 +122,6 @@ var doc = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.RepositoryList" "$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": { "/v2/{name}/blobs/uploads": {
"post": { "post": {
"description": "Create a new image blob/layer upload", "description": "Create a new image blob/layer upload",
@ -107,7 +182,7 @@ var doc = `{
"headers": { "headers": {
"Location": { "Location": {
"type": "string", "type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}" "description": "/v2/{name}/blobs/uploads/{session_id}"
}, },
"Range": { "Range": {
"type": "string", "type": "string",
@ -130,9 +205,9 @@ var doc = `{
} }
} }
}, },
"/v2/{name}/blobs/uploads/{uuid}": { "/v2/{name}/blobs/uploads/{session_id}": {
"get": { "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": [ "consumes": [
"application/json" "application/json"
], ],
@ -150,8 +225,8 @@ var doc = `{
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -196,8 +271,8 @@ var doc = `{
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
}, },
@ -249,8 +324,8 @@ var doc = `{
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -277,7 +352,7 @@ var doc = `{
} }
}, },
"patch": { "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": [ "consumes": [
"application/json" "application/json"
], ],
@ -295,8 +370,8 @@ var doc = `{
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -310,7 +385,7 @@ var doc = `{
"headers": { "headers": {
"Location": { "Location": {
"type": "string", "type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}" "description": "/v2/{name}/blobs/uploads/{session_id}"
}, },
"Range": { "Range": {
"type": "string", "type": "string",
@ -375,7 +450,6 @@ var doc = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest" "$ref": "#/definitions/api.ImageManifest"
} }
} }
@ -444,11 +518,10 @@ var doc = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest" "$ref": "#/definitions/api.ImageManifest"
}, },
"headers": { "headers": {
"api.DistContentDigestKey": { "constants.DistContentDigestKey": {
"type": "object", "type": "object",
"description": "OK" "description": "OK"
} }
@ -487,11 +560,10 @@ var doc = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest" "$ref": "#/definitions/api.ImageManifest"
}, },
"headers": { "headers": {
"api.DistContentDigestKey": { "constants.DistContentDigestKey": {
"type": "object", "type": "object",
"description": "OK" "description": "OK"
} }
@ -629,7 +701,7 @@ var doc = `{
"type": "string" "type": "string"
}, },
"headers": { "headers": {
"api.DistContentDigestKey": { "cosntants.DistContentDigestKey": {
"type": "object", "type": "object",
"description": "OK" "description": "OK"
} }
@ -642,7 +714,7 @@ var doc = `{
} }
}, },
"500": { "500": {
"description": "internal server error", "description": "internal server error\".",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@ -667,16 +739,35 @@ var doc = `{
"name": "name", "name": "name",
"in": "path", "in": "path",
"required": true "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": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageTags" "$ref": "#/definitions/api.ImageTags"
} }
}, },
"400": {
"description": "bad request\".",
"schema": {
"type": "string"
}
},
"404": { "404": {
"description": "not found", "description": "not found",
"schema": { "schema": {
@ -688,6 +779,9 @@ var doc = `{
} }
}, },
"definitions": { "definitions": {
"api.ExtensionList": {
"type": "object"
},
"api.ImageManifest": { "api.ImageManifest": {
"type": "object" "type": "object"
}, },
@ -729,7 +823,14 @@ type swaggerInfo struct {
} }
// SwaggerInfo holds exported Swagger Info so clients can modify it // 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{} type s struct{}
@ -737,7 +838,11 @@ func New() *s {
return &s{} return &s{}
} }
func (s *s) ReadDoc() string { 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{ t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string { "marshal": func(v interface{}) string {
a, _ := json.Marshal(v) a, _ := json.Marshal(v)
@ -749,7 +854,7 @@ func (s *s) ReadDoc() string {
} }
var tpl bytes.Buffer var tpl bytes.Buffer
if err := t.Execute(&tpl, SwaggerInfo); err != nil { if err := t.Execute(&tpl, sInfo); err != nil {
return doc return doc
} }

View file

@ -14,9 +14,62 @@
}, },
"version": "v0.1.0-dev" "version": "v0.1.0-dev"
}, },
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": { "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/": { "/v2/": {
"get": { "get": {
"description": "Check if this API version is supported", "description": "Check if this API version is supported",
@ -29,7 +82,7 @@
"summary": "Check API support", "summary": "Check API support",
"responses": { "responses": {
"200": { "200": {
"description": "ok", "description": "ok\".",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@ -51,7 +104,6 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.RepositoryList" "$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": { "/v2/{name}/blobs/uploads": {
"post": { "post": {
"description": "Create a new image blob/layer upload", "description": "Create a new image blob/layer upload",
@ -92,7 +164,7 @@
"headers": { "headers": {
"Location": { "Location": {
"type": "string", "type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}" "description": "/v2/{name}/blobs/uploads/{session_id}"
}, },
"Range": { "Range": {
"type": "string", "type": "string",
@ -115,9 +187,9 @@
} }
} }
}, },
"/v2/{name}/blobs/uploads/{uuid}": { "/v2/{name}/blobs/uploads/{session_id}": {
"get": { "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": [ "consumes": [
"application/json" "application/json"
], ],
@ -135,8 +207,8 @@
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -181,8 +253,8 @@
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
}, },
@ -234,8 +306,8 @@
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -262,7 +334,7 @@
} }
}, },
"patch": { "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": [ "consumes": [
"application/json" "application/json"
], ],
@ -280,8 +352,8 @@
}, },
{ {
"type": "string", "type": "string",
"description": "upload uuid", "description": "upload session_id",
"name": "uuid", "name": "session_id",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -295,7 +367,7 @@
"headers": { "headers": {
"Location": { "Location": {
"type": "string", "type": "string",
"description": "/v2/{name}/blobs/uploads/{uuid}" "description": "/v2/{name}/blobs/uploads/{session_id}"
}, },
"Range": { "Range": {
"type": "string", "type": "string",
@ -360,7 +432,6 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest" "$ref": "#/definitions/api.ImageManifest"
} }
} }
@ -429,11 +500,10 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest" "$ref": "#/definitions/api.ImageManifest"
}, },
"headers": { "headers": {
"api.DistContentDigestKey": { "constants.DistContentDigestKey": {
"type": "object", "type": "object",
"description": "OK" "description": "OK"
} }
@ -472,11 +542,10 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageManifest" "$ref": "#/definitions/api.ImageManifest"
}, },
"headers": { "headers": {
"api.DistContentDigestKey": { "constants.DistContentDigestKey": {
"type": "object", "type": "object",
"description": "OK" "description": "OK"
} }
@ -614,7 +683,7 @@
"type": "string" "type": "string"
}, },
"headers": { "headers": {
"api.DistContentDigestKey": { "cosntants.DistContentDigestKey": {
"type": "object", "type": "object",
"description": "OK" "description": "OK"
} }
@ -627,7 +696,7 @@
} }
}, },
"500": { "500": {
"description": "internal server error", "description": "internal server error\".",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@ -652,16 +721,35 @@
"name": "name", "name": "name",
"in": "path", "in": "path",
"required": true "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": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "object",
"$ref": "#/definitions/api.ImageTags" "$ref": "#/definitions/api.ImageTags"
} }
}, },
"400": {
"description": "bad request\".",
"schema": {
"type": "string"
}
},
"404": { "404": {
"description": "not found", "description": "not found",
"schema": { "schema": {
@ -673,6 +761,9 @@
} }
}, },
"definitions": { "definitions": {
"api.ExtensionList": {
"type": "object"
},
"api.ImageManifest": { "api.ImageManifest": {
"type": "object" "type": "object"
}, },

View file

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