mirror of
https://github.com/project-zot/zot.git
synced 2024-12-23 22:27:35 -05:00
21b7c69fd9
Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
659 lines
16 KiB
Go
659 lines
16 KiB
Go
//go:build search
|
|
// +build search
|
|
|
|
package cli //nolint:testpackage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/gorilla/mux"
|
|
godigest "github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.io/zot/pkg/test"
|
|
)
|
|
|
|
type RouteHandler struct {
|
|
Route string
|
|
// HandlerFunc is the HTTP handler function that receives a writer for output and an HTTP request as input.
|
|
HandlerFunc http.HandlerFunc
|
|
// AllowedMethods specifies the HTTP methods allowed for the current route.
|
|
AllowedMethods []string
|
|
}
|
|
|
|
// Routes is a map that associates HTTP paths to their corresponding HTTP handlers.
|
|
type HTTPRoutes []RouteHandler
|
|
|
|
func StartTestHTTPServer(routes HTTPRoutes, port string) *http.Server {
|
|
baseURL := test.GetBaseURL(port)
|
|
mux := mux.NewRouter()
|
|
|
|
mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := w.Write([]byte("{}"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}).Methods(http.MethodGet)
|
|
|
|
for _, routeHandler := range routes {
|
|
mux.HandleFunc(routeHandler.Route, routeHandler.HandlerFunc).Methods(routeHandler.AllowedMethods...)
|
|
}
|
|
|
|
server := &http.Server{ //nolint:gosec
|
|
Addr: fmt.Sprintf(":%s", port),
|
|
Handler: mux,
|
|
}
|
|
|
|
go func() {
|
|
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
|
return
|
|
}
|
|
}()
|
|
|
|
test.WaitTillServerReady(baseURL + "/test")
|
|
|
|
return server
|
|
}
|
|
|
|
func getDefaultSearchConf(baseURL string) searchConfig {
|
|
verifyTLS := false
|
|
debug := false
|
|
verbose := true
|
|
outputFormat := "text"
|
|
|
|
return searchConfig{
|
|
servURL: &baseURL,
|
|
resultWriter: io.Discard,
|
|
verifyTLS: &verifyTLS,
|
|
debug: &debug,
|
|
verbose: &verbose,
|
|
outputFormat: &outputFormat,
|
|
}
|
|
}
|
|
|
|
func TestDoHTTPRequest(t *testing.T) {
|
|
Convey("doHTTPRequest nil result pointer", t, func() {
|
|
port := test.GetFreePort()
|
|
server := StartTestHTTPServer(nil, port)
|
|
defer server.Close()
|
|
|
|
url := fmt.Sprintf("http://127.0.0.1:%s/asd", port)
|
|
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, url, nil)
|
|
So(err, ShouldBeNil)
|
|
|
|
So(func() { _, _ = doHTTPRequest(req, false, false, nil, io.Discard) }, ShouldNotPanic)
|
|
})
|
|
|
|
Convey("doHTTPRequest bad return json", t, func() {
|
|
port := test.GetFreePort()
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/test",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := w.Write([]byte("bad json"))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
url := fmt.Sprintf("http://127.0.0.1:%s/test", port)
|
|
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
|
|
So(err, ShouldBeNil)
|
|
|
|
So(func() { _, _ = doHTTPRequest(req, false, false, &ispec.Manifest{}, io.Discard) }, ShouldNotPanic)
|
|
})
|
|
|
|
Convey("makeGraphQLRequest bad request context", t, func() {
|
|
err := makeGraphQLRequest(nil, "", "", "", "", false, false, nil, io.Discard) //nolint:staticcheck
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("makeHEADRequest bad request context", t, func() {
|
|
_, err := makeHEADRequest(nil, "", "", "", false, false) //nolint:staticcheck
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("makeGETRequest bad request context", t, func() {
|
|
_, err := makeGETRequest(nil, "", "", "", false, false, nil, io.Discard) //nolint:staticcheck
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("fetchImageManifestStruct errors", t, func() {
|
|
port := test.GetFreePort()
|
|
baseURL := test.GetBaseURL(port)
|
|
searchConf := getDefaultSearchConf(baseURL)
|
|
|
|
// 404 erorr will appear
|
|
server := StartTestHTTPServer(HTTPRoutes{}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/tag"
|
|
|
|
_, err := fetchImageManifestStruct(context.Background(), &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "repo",
|
|
tagName: "tag",
|
|
config: searchConf,
|
|
})
|
|
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("fetchManifestStruct errors", t, func() {
|
|
port := test.GetFreePort()
|
|
baseURL := test.GetBaseURL(port)
|
|
searchConf := getDefaultSearchConf(baseURL)
|
|
|
|
Convey("makeGETRequest manifest error, context is done", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{}, port)
|
|
defer server.Close()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
cancel()
|
|
|
|
_, err := fetchManifestStruct(ctx, "repo", "tag", searchConf,
|
|
"", "")
|
|
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("makeGETRequest manifest error, context is not done", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{}, port)
|
|
defer server.Close()
|
|
|
|
_, err := fetchManifestStruct(context.Background(), "repo", "tag", searchConf,
|
|
"", "")
|
|
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("makeGETRequest config error, context is not done", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := w.Write([]byte(`{"config":{"digest":"digest","size":0}}`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
_, err := fetchManifestStruct(context.Background(), "repo", "tag", searchConf,
|
|
"", "")
|
|
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Platforms on config", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := w.Write([]byte(`
|
|
{
|
|
"config":{
|
|
"digest":"digest",
|
|
"size":0,
|
|
"platform" : {
|
|
"os": "",
|
|
"architecture": "",
|
|
"variant": ""
|
|
}
|
|
}
|
|
}
|
|
`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
{
|
|
Route: "/v2/{name}/blobs/{digest}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := w.Write([]byte(`
|
|
{
|
|
"architecture": "arch",
|
|
"os": "os",
|
|
"variant": "var"
|
|
}
|
|
`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
_, err := fetchManifestStruct(context.Background(), "repo", "tag", searchConf,
|
|
"", "")
|
|
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("isNotationSigned error", func() {
|
|
isSigned := isNotationSigned(context.Background(), "repo", "digest", searchConf,
|
|
"", "")
|
|
So(isSigned, ShouldBeFalse)
|
|
})
|
|
|
|
Convey("fetchImageIndexStruct no errors", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(writer http.ResponseWriter, req *http.Request) {
|
|
vars := mux.Vars(req)
|
|
|
|
if vars["reference"] == "indexRef" {
|
|
writer.Header().Add("docker-content-digest", godigest.FromString("t").String())
|
|
_, err := writer.Write([]byte(`
|
|
{
|
|
"manifests": [
|
|
{
|
|
"digest": "manifestRef",
|
|
"platform": {
|
|
"architecture": "arch",
|
|
"os": "os",
|
|
"variant": "var"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else if vars["reference"] == "manifestRef" {
|
|
_, err := writer.Write([]byte(`
|
|
{
|
|
"config":{
|
|
"digest":"digest",
|
|
"size":0
|
|
}
|
|
}
|
|
`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
{
|
|
Route: "/v2/{name}/blobs/{digest}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := w.Write([]byte(`{}`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/indexRef"
|
|
|
|
imageStruct, err := fetchImageIndexStruct(context.Background(), &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "repo",
|
|
tagName: "tag",
|
|
config: searchConf,
|
|
})
|
|
So(err, ShouldBeNil)
|
|
So(imageStruct, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("fetchImageIndexStruct makeGETRequest errors context done", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{}, port)
|
|
defer server.Close()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
cancel()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/indexRef"
|
|
|
|
imageStruct, err := fetchImageIndexStruct(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "repo",
|
|
tagName: "tag",
|
|
config: searchConf,
|
|
})
|
|
So(err, ShouldNotBeNil)
|
|
So(imageStruct, ShouldBeNil)
|
|
})
|
|
|
|
Convey("fetchImageIndexStruct makeGETRequest errors context not done", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/indexRef"
|
|
|
|
imageStruct, err := fetchImageIndexStruct(context.Background(), &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "repo",
|
|
tagName: "tag",
|
|
config: searchConf,
|
|
})
|
|
So(err, ShouldNotBeNil)
|
|
So(imageStruct, ShouldBeNil)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestDoJobErrors(t *testing.T) {
|
|
port := test.GetFreePort()
|
|
baseURL := test.GetBaseURL(port)
|
|
searchConf := getDefaultSearchConf(baseURL)
|
|
|
|
reqPool := &requestsPool{
|
|
jobs: make(chan *httpJob),
|
|
done: make(chan struct{}),
|
|
wtgrp: &sync.WaitGroup{},
|
|
outputCh: make(chan stringResult),
|
|
}
|
|
|
|
Convey("Do Job errors", t, func() {
|
|
reqPool.wtgrp.Add(1)
|
|
|
|
Convey("Do Job makeHEADRequest error context done", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/manifestRef"
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
cancel()
|
|
|
|
reqPool.doJob(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "",
|
|
tagName: "",
|
|
config: searchConf,
|
|
})
|
|
})
|
|
|
|
Convey("Do Job makeHEADRequest error context not done", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/manifestRef"
|
|
|
|
ctx := context.Background()
|
|
|
|
go reqPool.doJob(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "",
|
|
tagName: "",
|
|
config: searchConf,
|
|
})
|
|
|
|
result := <-reqPool.outputCh
|
|
So(result.Err, ShouldNotBeNil)
|
|
So(result.StrValue, ShouldResemble, "")
|
|
})
|
|
|
|
Convey("Do Job fetchManifestStruct errors context canceled", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", ispec.MediaTypeImageManifest)
|
|
_, err := w.Write([]byte(""))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodHead},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/manifestRef"
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
cancel()
|
|
// context not canceled
|
|
|
|
reqPool.doJob(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "",
|
|
tagName: "",
|
|
config: searchConf,
|
|
})
|
|
})
|
|
|
|
Convey("Do Job fetchManifestStruct errors context not canceled", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", ispec.MediaTypeImageManifest)
|
|
_, err := w.Write([]byte(""))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodHead},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/manifestRef"
|
|
|
|
ctx := context.Background()
|
|
|
|
go reqPool.doJob(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "",
|
|
tagName: "",
|
|
config: searchConf,
|
|
})
|
|
|
|
result := <-reqPool.outputCh
|
|
So(result.Err, ShouldNotBeNil)
|
|
So(result.StrValue, ShouldResemble, "")
|
|
})
|
|
|
|
Convey("Do Job fetchIndexStruct errors context canceled", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", ispec.MediaTypeImageIndex)
|
|
_, err := w.Write([]byte(""))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodHead},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/indexRef"
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
cancel()
|
|
// context not canceled
|
|
|
|
reqPool.doJob(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "",
|
|
tagName: "",
|
|
config: searchConf,
|
|
})
|
|
})
|
|
|
|
Convey("Do Job fetchIndexStruct errors context not canceled", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", ispec.MediaTypeImageIndex)
|
|
_, err := w.Write([]byte(""))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodHead},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/indexRef"
|
|
|
|
ctx := context.Background()
|
|
|
|
go reqPool.doJob(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "",
|
|
tagName: "",
|
|
config: searchConf,
|
|
})
|
|
|
|
result := <-reqPool.outputCh
|
|
So(result.Err, ShouldNotBeNil)
|
|
So(result.StrValue, ShouldResemble, "")
|
|
})
|
|
Convey("Do Job fetchIndexStruct not supported content type", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "some-media-type")
|
|
_, err := w.Write([]byte(""))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodHead},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
|
|
URL := baseURL + "/v2/repo/manifests/indexRef"
|
|
|
|
ctx := context.Background()
|
|
|
|
reqPool.doJob(ctx, &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "",
|
|
tagName: "",
|
|
config: searchConf,
|
|
})
|
|
})
|
|
|
|
Convey("Media type is MediaTypeImageIndex image.string erorrs", func() {
|
|
server := StartTestHTTPServer(HTTPRoutes{
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", ispec.MediaTypeImageIndex)
|
|
w.Header().Add("docker-content-digest", godigest.FromString("t").String())
|
|
|
|
_, err := w.Write([]byte(""))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodHead},
|
|
},
|
|
{
|
|
Route: "/v2/{name}/manifests/{reference}",
|
|
HandlerFunc: func(writer http.ResponseWriter, req *http.Request) {
|
|
vars := mux.Vars(req)
|
|
|
|
if vars["reference"] == "indexRef" {
|
|
writer.Header().Add("docker-content-digest", godigest.FromString("t").String())
|
|
|
|
_, err := writer.Write([]byte(`{"manifests": [{"digest": "manifestRef"}]}`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if vars["reference"] == "manifestRef" {
|
|
writer.Header().Add("docker-content-digest", godigest.FromString("t").String())
|
|
|
|
_, err := writer.Write([]byte(`{"config": {"digest": "confDigest"}}`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
{
|
|
Route: "/v2/{name}/blobs/{digest}",
|
|
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
_, err := w.Write([]byte(`{}`))
|
|
if err != nil {
|
|
return
|
|
}
|
|
},
|
|
AllowedMethods: []string{http.MethodGet},
|
|
},
|
|
}, port)
|
|
defer server.Close()
|
|
URL := baseURL + "/v2/repo/manifests/indexRef"
|
|
|
|
go reqPool.doJob(context.Background(), &httpJob{
|
|
url: URL,
|
|
username: "",
|
|
password: "",
|
|
imageName: "repo",
|
|
tagName: "indexRef",
|
|
config: searchConf,
|
|
})
|
|
|
|
result := <-reqPool.outputCh
|
|
So(result.Err, ShouldNotBeNil)
|
|
So(result.StrValue, ShouldResemble, "")
|
|
})
|
|
})
|
|
}
|