Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import rest of tests from kinvolk/ocicert #39

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 232 additions & 6 deletions test/dist/dist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
package dist

import (
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"testing"

"github.com/opencontainers/distribution-spec/test/pkg/auth"
distp "github.com/opencontainers/distribution-spec/test/pkg/distp"
"github.com/opencontainers/distribution-spec/test/pkg/image"
)

var (
Expand All @@ -35,8 +38,6 @@ var (

func init() {
homeDir = os.Getenv("HOME")

regURL = regAuthCtx.RegURL
}

func TestCheckAPIVersion(t *testing.T) {
Expand All @@ -45,6 +46,7 @@ func TestCheckAPIVersion(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regAuthCtx.Scope.RemoteName = reqPath
regAuthCtx.Scope.Actions = "pull"
regURL := regAuthCtx.RegURL

indexServer := auth.GetIndexServer(regURL)

Expand All @@ -64,7 +66,7 @@ func TestCheckAPIVersion(t *testing.T) {
}
}

func TestPullManifest(t *testing.T) {
func getDigestFromManifest(regURL, testImageName, testRefName string) (string, error) {
indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)
Expand All @@ -74,24 +76,191 @@ func TestPullManifest(t *testing.T) {
regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "pull"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
return "", fmt.Errorf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL := "https://" + indexServer + "/v2/" + reqPath

res, err := regAuthCtx.GetResponse(inputURL, "HEAD", nil, []int{http.StatusOK})
if err != nil {
return "", fmt.Errorf("got an unexpected reply: %v", err)
}

return res.Header.Get(distp.ContentDigest), nil
}

func testUploadLayer(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL
indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)
reqPath := filepath.Join(remoteName, "blobs/uploads", testRefName)

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "push"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

// 1. POST
// Send a POST request without any body specified.
postURL := "https://" + indexServer + "/v2/" + reqPath

res, err := regAuthCtx.GetResponse(postURL, "POST", nil, []int{http.StatusOK, http.StatusAccepted})
if err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}

uuid := res.Header.Get(distp.UploadUuidKey)

// 2. PATCH
// Generate a 100-byte blob of a randomly generated string.
// Send a PATCH request with the blob.
blob := image.GenRandomBlob(100)

if _, err := regAuthCtx.GetResponse(postURL, "PATCH", strings.NewReader(blob),
[]int{http.StatusOK, http.StatusAccepted}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}

// 3. PUT
// Generate a blob's digest, generated as a sha256 checksum of the blob.
// Send a PUT request with a "digest=..." option appended to its URL.
digest := image.GetHash(blob)
putURL := "https://" + indexServer + "/v2/" + reqPath + "/" + uuid + "?digest=" + digest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm wondering if a next step could be to have a golang package much like https://github.com/docker/distribution/blob/master/registry/api/v2/descriptors.go#L237 that clients could import and this test suite could also use to validate against.


if _, err := regAuthCtx.GetResponse(putURL, "PUT", strings.NewReader(blob),
[]int{http.StatusCreated}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}

func testPushManifest(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL
indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)
reqPath := filepath.Join(remoteName, "manifests", testRefName)

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "push"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL := "https://" + indexServer + "/v2/" + reqPath

if _, err := regAuthCtx.GetResponse(inputURL, "GET", nil, []int{http.StatusOK}); err != nil {
if _, err := regAuthCtx.GetResponse(inputURL, "PUT", nil, []int{http.StatusCreated}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}

func TestPushManifest(t *testing.T) {
func testPullManifest(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL
indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)
reqPath := filepath.Join(remoteName, "manifests", testRefName)

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "pull"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL := "https://" + indexServer + "/v2/" + reqPath

if _, err := regAuthCtx.GetResponse(inputURL, "GET", nil, []int{http.StatusOK}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}

reqPath = filepath.Join(remoteName, "tags/list")

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "pull"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL = "https://" + indexServer + "/v2/" + reqPath

if _, err := regAuthCtx.GetResponse(inputURL, "GET", nil, []int{http.StatusOK}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}

func testPullLayer(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL
indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)

testDigest, err := getDigestFromManifest(regURL, testImageName, testRefName)
if err != nil {
t.Fatalf("failed to get digest from %s: %v", indexServer, err)
}

reqPath := filepath.Join(remoteName, "blobs", testDigest)

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "pull"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL := "https://" + indexServer + "/v2/" + reqPath

if _, err := regAuthCtx.GetResponse(inputURL, "GET", nil, []int{http.StatusOK}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}

func testDeleteLayer(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL
indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)

testDigest, err := getDigestFromManifest(regURL, testImageName, testRefName)
if err != nil {
t.Fatalf("failed to get digest from %s: %v", indexServer, err)
}

reqPath := filepath.Join(remoteName, "blobs", testDigest)

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "push"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL := "https://" + indexServer + "/v2/" + reqPath

if _, err := regAuthCtx.GetResponse(inputURL, "DELETE", nil, []int{http.StatusAccepted}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}

func testDeleteManifest(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL
indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)
reqPath := filepath.Join(remoteName, "manifests", testRefName)

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "push"

Expand All @@ -101,7 +270,64 @@ func TestPushManifest(t *testing.T) {

inputURL := "https://" + indexServer + "/v2/" + reqPath

if _, err := regAuthCtx.GetResponse(inputURL, "PUT", nil, []int{http.StatusOK}); err != nil {
if _, err := regAuthCtx.GetResponse(inputURL, "DELETE", nil, []int{http.StatusAccepted}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}

func TestPushPullLayer(t *testing.T) {
testUploadLayer(t)
testPushManifest(t)
testPullManifest(t)
testPullLayer(t)
testDeleteLayer(t)
testDeleteManifest(t)
}

func TestListRepos(t *testing.T) {
reqPath := "_catalog"

regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL

regAuthCtx.Scope.RemoteName = reqPath
regAuthCtx.Scope.Actions = "pull"

indexServer := auth.GetIndexServer(regURL)

// NOTE: it will fail when testing against docker.io, as '/v2/_catalog' endpoint
// will not be supported.
if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL := "https://" + indexServer + "/v2/" + reqPath

_, err := regAuthCtx.GetResponse(inputURL, "GET", nil, []int{http.StatusOK})
if err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}

func TestListTags(t *testing.T) {
regAuthCtx := auth.NewRegAuthContext()
regURL := regAuthCtx.RegURL

indexServer := auth.GetIndexServer(regURL)

remoteName := filepath.Join(auth.DefaultRepoPrefix, testImageName)
reqPath := filepath.Join(remoteName, "tags/")

regAuthCtx.Scope.RemoteName = remoteName
regAuthCtx.Scope.Actions = "pull"

if err := regAuthCtx.PrepareAuth(indexServer); err != nil {
t.Fatalf("failed to prepare auth to %s for %s: %v", indexServer, reqPath, err)
}

inputURL := "https://" + indexServer + "/v2/" + reqPath

if _, err := regAuthCtx.GetResponse(inputURL, "GET", nil, []int{http.StatusOK}); err != nil {
t.Fatalf("got an unexpected reply: %v", err)
}
}
1 change: 1 addition & 0 deletions test/pkg/distp/distp.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ const (
DistAPIVersionValue string = "registry/2.0"

UploadUuidKey string = "Docker-Upload-Uuid"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changed recently. Not sure to test against the docker values as well. Perhaps this could be a struct, to then have an array of them to iterate over.

ContentDigest string = "Docker-Content-Digest"
)
20 changes: 20 additions & 0 deletions test/pkg/image/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,23 @@
// limitations under the License.

package image

import (
"crypto/sha256"
"math/rand"
"strconv"
)

func GenRandomBlob(blobLen int) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually to get a "blob" or just a simple string?

blob := ""
for i := 0; i < blobLen; i++ {
blob += strconv.Itoa(rand.Intn(9))
}
return blob
}

func GetHash(inputStr string) string {
h := sha256.New()
h.Write([]byte(inputStr))
return string(h.Sum(nil))
}