mirror of https://github.com/docker/docs.git
307 lines
9.0 KiB
Go
307 lines
9.0 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
_ "github.com/docker/distribution/registry/auth/silly"
|
|
"github.com/docker/notary"
|
|
"github.com/docker/notary/server/storage"
|
|
"github.com/docker/notary/tuf/data"
|
|
"github.com/docker/notary/tuf/signed"
|
|
"github.com/docker/notary/tuf/store"
|
|
"github.com/docker/notary/tuf/testutils"
|
|
tufutils "github.com/docker/notary/tuf/utils"
|
|
"github.com/docker/notary/utils"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
func TestRunBadAddr(t *testing.T) {
|
|
err := Run(
|
|
context.Background(),
|
|
Config{
|
|
Addr: "testAddr",
|
|
Trust: signed.NewEd25519(),
|
|
},
|
|
)
|
|
require.Error(t, err, "Passed bad addr, Run should have failed")
|
|
}
|
|
|
|
func TestRunReservedPort(t *testing.T) {
|
|
ctx, _ := context.WithCancel(context.Background())
|
|
|
|
err := Run(
|
|
ctx,
|
|
Config{
|
|
Addr: "localhost:80",
|
|
Trust: signed.NewEd25519(),
|
|
},
|
|
)
|
|
|
|
require.Error(t, err)
|
|
require.IsType(t, &net.OpError{}, err)
|
|
require.True(
|
|
t,
|
|
strings.Contains(err.Error(), "bind: permission denied"),
|
|
"Received unexpected err: %s",
|
|
err.Error(),
|
|
)
|
|
}
|
|
|
|
func TestRepoPrefixMatches(t *testing.T) {
|
|
gun := "docker.io/notary"
|
|
meta, cs, err := testutils.NewRepoMetadata(gun)
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.WithValue(context.Background(), "metaStore", storage.NewMemStorage())
|
|
ctx = context.WithValue(ctx, "keyAlgorithm", data.ED25519Key)
|
|
|
|
snChecksumBytes := sha256.Sum256(meta[data.CanonicalSnapshotRole])
|
|
|
|
// successful gets
|
|
handler := RootHandler(nil, ctx, cs, nil, nil, []string{"docker.io"})
|
|
ts := httptest.NewServer(handler)
|
|
|
|
url := fmt.Sprintf("%s/v2/%s/_trust/tuf/", ts.URL, gun)
|
|
uploader, err := store.NewHTTPStore(url, "", "json", "key", http.DefaultTransport)
|
|
require.NoError(t, err)
|
|
|
|
// uploading is cool
|
|
require.NoError(t, uploader.SetMultiMeta(meta))
|
|
// getting is cool
|
|
_, err = uploader.GetMeta(data.CanonicalSnapshotRole, notary.MaxDownloadSize)
|
|
require.NoError(t, err)
|
|
|
|
_, err = uploader.GetMeta(
|
|
tufutils.ConsistentName(data.CanonicalSnapshotRole, snChecksumBytes[:]), notary.MaxDownloadSize)
|
|
require.NoError(t, err)
|
|
|
|
_, err = uploader.GetKey(data.CanonicalTimestampRole)
|
|
require.NoError(t, err)
|
|
|
|
// the httpstore doesn't actually delete all, so we do it manually
|
|
req, err := http.NewRequest("DELETE", url, nil)
|
|
require.NoError(t, err)
|
|
res, err := http.DefaultTransport.RoundTrip(req)
|
|
require.NoError(t, err)
|
|
defer res.Body.Close()
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
}
|
|
|
|
func TestRepoPrefixDoesNotMatch(t *testing.T) {
|
|
gun := "docker.io/notary"
|
|
meta, cs, err := testutils.NewRepoMetadata(gun)
|
|
require.NoError(t, err)
|
|
s := storage.NewMemStorage()
|
|
|
|
ctx := context.WithValue(context.Background(), "metaStore", s)
|
|
ctx = context.WithValue(ctx, "keyAlgorithm", data.ED25519Key)
|
|
|
|
snChecksumBytes := sha256.Sum256(meta[data.CanonicalSnapshotRole])
|
|
|
|
// successful gets
|
|
handler := RootHandler(nil, ctx, cs, nil, nil, []string{"nope"})
|
|
ts := httptest.NewServer(handler)
|
|
|
|
url := fmt.Sprintf("%s/v2/%s/_trust/tuf/", ts.URL, gun)
|
|
uploader, err := store.NewHTTPStore(url, "", "json", "key", http.DefaultTransport)
|
|
require.NoError(t, err)
|
|
|
|
require.Error(t, uploader.SetMultiMeta(meta))
|
|
|
|
// update the storage so we don't fail just because the metadata is missing
|
|
for _, roleName := range data.BaseRoles {
|
|
require.NoError(t, s.UpdateCurrent(gun, storage.MetaUpdate{
|
|
Role: roleName,
|
|
Data: meta[roleName],
|
|
Version: 1,
|
|
}))
|
|
}
|
|
|
|
_, err = uploader.GetMeta(data.CanonicalSnapshotRole, notary.MaxDownloadSize)
|
|
require.Error(t, err)
|
|
|
|
_, err = uploader.GetMeta(
|
|
tufutils.ConsistentName(data.CanonicalSnapshotRole, snChecksumBytes[:]), notary.MaxDownloadSize)
|
|
require.Error(t, err)
|
|
|
|
_, err = uploader.GetKey(data.CanonicalTimestampRole)
|
|
require.Error(t, err)
|
|
|
|
// the httpstore doesn't actually delete all, so we do it manually
|
|
req, err := http.NewRequest("DELETE", url, nil)
|
|
require.NoError(t, err)
|
|
res, err := http.DefaultTransport.RoundTrip(req)
|
|
require.NoError(t, err)
|
|
defer res.Body.Close()
|
|
require.Equal(t, http.StatusNotFound, res.StatusCode)
|
|
}
|
|
|
|
func TestMetricsEndpoint(t *testing.T) {
|
|
handler := RootHandler(nil, context.Background(), signed.NewEd25519(),
|
|
nil, nil, nil)
|
|
ts := httptest.NewServer(handler)
|
|
defer ts.Close()
|
|
|
|
res, err := http.Get(ts.URL + "/metrics")
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
}
|
|
|
|
// GetKeys supports only the timestamp and snapshot key endpoints
|
|
func TestGetKeysEndpoint(t *testing.T) {
|
|
ctx := context.WithValue(
|
|
context.Background(), "metaStore", storage.NewMemStorage())
|
|
ctx = context.WithValue(ctx, "keyAlgorithm", data.ED25519Key)
|
|
|
|
handler := RootHandler(nil, ctx, signed.NewEd25519(), nil, nil, nil)
|
|
ts := httptest.NewServer(handler)
|
|
defer ts.Close()
|
|
|
|
rolesToStatus := map[string]int{
|
|
data.CanonicalTimestampRole: http.StatusOK,
|
|
data.CanonicalSnapshotRole: http.StatusOK,
|
|
data.CanonicalTargetsRole: http.StatusNotFound,
|
|
data.CanonicalRootRole: http.StatusNotFound,
|
|
"somerandomrole": http.StatusNotFound,
|
|
}
|
|
|
|
for role, expectedStatus := range rolesToStatus {
|
|
res, err := http.Get(
|
|
fmt.Sprintf("%s/v2/gun/_trust/tuf/%s.key", ts.URL, role))
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedStatus, res.StatusCode)
|
|
}
|
|
}
|
|
|
|
// This just checks the URL routing is working correctly and cache headers are set correctly.
|
|
// More detailed tests for this path including negative
|
|
// tests are located in /server/handlers/
|
|
func TestGetRoleByHash(t *testing.T) {
|
|
store := storage.NewMemStorage()
|
|
|
|
ts := data.SignedTimestamp{
|
|
Signatures: make([]data.Signature, 0),
|
|
Signed: data.Timestamp{
|
|
SignedCommon: data.SignedCommon{
|
|
Type: data.TUFTypes[data.CanonicalTimestampRole],
|
|
Version: 1,
|
|
Expires: data.DefaultExpires(data.CanonicalTimestampRole),
|
|
},
|
|
},
|
|
}
|
|
j, err := json.Marshal(&ts)
|
|
require.NoError(t, err)
|
|
store.UpdateCurrent("gun", storage.MetaUpdate{
|
|
Role: data.CanonicalTimestampRole,
|
|
Version: 1,
|
|
Data: j,
|
|
})
|
|
checksumBytes := sha256.Sum256(j)
|
|
checksum := hex.EncodeToString(checksumBytes[:])
|
|
|
|
// create and add a newer timestamp. We're going to try and request
|
|
// the older version we created above.
|
|
ts = data.SignedTimestamp{
|
|
Signatures: make([]data.Signature, 0),
|
|
Signed: data.Timestamp{
|
|
SignedCommon: data.SignedCommon{
|
|
Type: data.TUFTypes[data.CanonicalTimestampRole],
|
|
Version: 2,
|
|
Expires: data.DefaultExpires(data.CanonicalTimestampRole),
|
|
},
|
|
},
|
|
}
|
|
newTS, err := json.Marshal(&ts)
|
|
require.NoError(t, err)
|
|
store.UpdateCurrent("gun", storage.MetaUpdate{
|
|
Role: data.CanonicalTimestampRole,
|
|
Version: 1,
|
|
Data: newTS,
|
|
})
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(), "metaStore", store)
|
|
|
|
ctx = context.WithValue(ctx, "keyAlgorithm", data.ED25519Key)
|
|
|
|
ccc := utils.NewCacheControlConfig(10, false)
|
|
handler := RootHandler(nil, ctx, signed.NewEd25519(), ccc, ccc, nil)
|
|
serv := httptest.NewServer(handler)
|
|
defer serv.Close()
|
|
|
|
res, err := http.Get(fmt.Sprintf(
|
|
"%s/v2/gun/_trust/tuf/%s.%s.json",
|
|
serv.URL,
|
|
data.CanonicalTimestampRole,
|
|
checksum,
|
|
))
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
// if content is equal, checksums are guaranteed to be equal
|
|
verifyGetResponse(t, res, j)
|
|
}
|
|
|
|
// This just checks the URL routing is working correctly and cache headers are set correctly.
|
|
// More detailed tests for this path including negative
|
|
// tests are located in /server/handlers/
|
|
func TestGetCurrentRole(t *testing.T) {
|
|
store := storage.NewMemStorage()
|
|
metadata, _, err := testutils.NewRepoMetadata("gun")
|
|
require.NoError(t, err)
|
|
|
|
// need both the snapshot and the timestamp, because when getting the current
|
|
// timestamp the server checks to see if it's out of date (there's a new snapshot)
|
|
// and if so, generates a new one
|
|
store.UpdateCurrent("gun", storage.MetaUpdate{
|
|
Role: data.CanonicalSnapshotRole,
|
|
Version: 1,
|
|
Data: metadata[data.CanonicalSnapshotRole],
|
|
})
|
|
store.UpdateCurrent("gun", storage.MetaUpdate{
|
|
Role: data.CanonicalTimestampRole,
|
|
Version: 1,
|
|
Data: metadata[data.CanonicalTimestampRole],
|
|
})
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(), "metaStore", store)
|
|
|
|
ctx = context.WithValue(ctx, "keyAlgorithm", data.ED25519Key)
|
|
|
|
ccc := utils.NewCacheControlConfig(10, false)
|
|
handler := RootHandler(nil, ctx, signed.NewEd25519(), ccc, ccc, nil)
|
|
serv := httptest.NewServer(handler)
|
|
defer serv.Close()
|
|
|
|
res, err := http.Get(fmt.Sprintf(
|
|
"%s/v2/gun/_trust/tuf/%s.json",
|
|
serv.URL,
|
|
data.CanonicalTimestampRole,
|
|
))
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
verifyGetResponse(t, res, metadata[data.CanonicalTimestampRole])
|
|
}
|
|
|
|
// Verifies that the body is as expected and that there are cache control headers
|
|
func verifyGetResponse(t *testing.T, r *http.Response, expectedBytes []byte) {
|
|
body, err := ioutil.ReadAll(r.Body)
|
|
require.NoError(t, err)
|
|
require.True(t, bytes.Equal(expectedBytes, body))
|
|
|
|
require.NotEqual(t, "", r.Header.Get("Cache-Control"))
|
|
require.NotEqual(t, "", r.Header.Get("Last-Modified"))
|
|
require.Equal(t, "", r.Header.Get("Pragma"))
|
|
}
|