quorate/services/mirrored-image/controller/registry_test.go

163 lines
5.7 KiB
Go
Raw Permalink Normal View History

package main
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
const childDigest = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
func TestPhysicalPresence(t *testing.T) {
t.Parallel()
for _, status := range []int{200, 404, 401, 403, 429, 500, 502} {
t.Run(fmt.Sprint(status), func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
if request.URL.Path != "/api/v1/repository/candidates/pause/manifest/"+testDigest+"/security" || request.URL.Query().Get("vulnerabilities") != "false" {
t.Errorf("unexpected lookup: %s", request.URL)
}
if request.Header.Get("Authorization") != "Bearer reader-token" {
t.Error("lookup did not use reader authority")
}
response.WriteHeader(status)
_, _ = fmt.Fprint(response, `{"status":"queued"}`)
}))
defer server.Close()
api, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
reader := &quayReader{api: api, client: server.Client(), candidates: "reader-token"}
present, err := reader.physicallyPresent(context.Background(), "candidates/pause", testDigest)
if present != (status == 200) || (err != nil) != (status != 200 && status != 404) {
t.Fatalf("HTTP %d: present=%t err=%v", status, present, err)
}
})
}
}
func TestQuayScanAndExistence(t *testing.T) {
t.Parallel()
child := fmt.Sprintf(`{"schemaVersion":2,"layers":[{"digest":%q}]}`, testDigest)
childDigest := fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(child)))
index := fmt.Sprintf(`{"manifests":[{"digest":%q}]}`, childDigest)
testDigest := fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(index)))
severity := "Low"
var server *httptest.Server
server = httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
switch {
case request.URL.Path == "/v2/candidates/pause/manifests/"+childDigest:
_, _ = fmt.Fprint(response, child)
case request.URL.Path == "/v2/auth":
username, password, found := request.BasicAuth()
if !found || username != oauthUser || (password != "candidate-token" && password != "mirror-token") {
http.Error(response, "unauthorized", http.StatusUnauthorized)
return
}
response.Header().Set("Content-Type", "application/json")
if _, err := fmt.Fprintf(response, `{"token":%q}`, password+"-registry"); err != nil {
t.Errorf("write token response: %v", err)
}
case request.URL.Path == "/v2/candidates/pause/manifests/"+testDigest || request.URL.Path == "/v2/mirror/pause/manifests/"+testDigest:
expected := "Bearer candidate-token-registry"
if strings.Contains(request.URL.Path, "/mirror/") {
expected = "Bearer mirror-token-registry"
}
if request.Header.Get("Authorization") != expected {
response.Header().Set("WWW-Authenticate", `Bearer realm="`+server.URL+`/v2/auth",service="fixture",scope="repository:test:pull"`)
response.WriteHeader(http.StatusUnauthorized)
return
}
if strings.Contains(request.URL.Path, "/mirror/") {
response.Header().Set("Content-Type", "application/vnd.oci.image.manifest.v1+json")
if _, err := fmt.Fprint(response, `{"schemaVersion":2}`); err != nil {
t.Errorf("write manifest response: %v", err)
}
return
}
response.Header().Set("Content-Type", "application/vnd.oci.image.index.v1+json")
if _, err := fmt.Fprintf(response, `{"manifests":[{"digest":%q}]}`, childDigest); err != nil {
t.Errorf("write index response: %v", err)
}
case strings.HasSuffix(request.URL.Path, "/security"):
if request.Header.Get("Authorization") != "Bearer candidate-token" || request.URL.Query().Get("vulnerabilities") != "true" {
http.Error(response, "unauthorized", http.StatusUnauthorized)
return
}
data := inventoryFixture()
data.Inventory.Hash = childDigest
data.Layer.Name = childDigest
(*data.Layer.Features)[0].Vulnerabilities = []vulnerability{{Severity: severity}}
if err := json.NewEncoder(response).Encode(securityDoc{Status: "scanned", Data: data}); err != nil {
t.Errorf("write scan response: %v", err)
}
default:
http.NotFound(response, request)
}
}))
defer server.Close()
api, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
reader := &quayReader{
api: api,
registry: server.Listener.Addr().String(),
client: server.Client(),
candidates: "candidate-token",
mirror: "mirror-token",
}
phase, err := reader.scan(context.Background(), "candidates/pause", testDigest, "High")
if err != nil {
t.Fatal(err)
}
if phase != scanAccepted {
t.Fatalf("unexpected accepted phase %q", phase)
}
severity = "Critical"
phase, err = reader.scan(context.Background(), "candidates/pause", testDigest, "High")
if err != nil {
t.Fatal(err)
}
if phase != scanRejected {
t.Fatalf("unexpected rejected phase %q", phase)
}
severity = "Unknown"
phase, err = reader.scan(context.Background(), "candidates/pause", testDigest, "Critical")
if err != nil || phase != scanRejected {
t.Fatalf("unknown severity: %s %v", phase, err)
}
present, err := reader.exists(context.Background(), "mirror/pause", testDigest)
if err != nil {
t.Fatal(err)
}
if !present {
t.Fatal("promoted manifest was not observed")
}
present, err = reader.exists(context.Background(), "candidates/missing", testDigest)
if err != nil {
t.Fatal(err)
}
if present {
t.Fatal("missing manifest was observed")
}
}
func TestSingleManifestScanDigest(t *testing.T) {
t.Parallel()
digests, err := scanDigests([]byte(`{"schemaVersion":2}`), testDigest)
if err != nil {
t.Fatal(err)
}
if len(digests) != 1 || digests[0] != testDigest {
t.Fatalf("unexpected scan digests %#v", digests)
}
}