mirror of https://github.com/docker/docs.git
190 lines
5.6 KiB
Go
190 lines
5.6 KiB
Go
package client
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"github.com/docker/notary/cryptoservice"
|
|
"github.com/docker/notary/passphrase"
|
|
pb "github.com/docker/notary/proto"
|
|
"github.com/docker/notary/signer"
|
|
"github.com/docker/notary/signer/api"
|
|
"github.com/docker/notary/trustmanager"
|
|
"github.com/docker/notary/tuf/data"
|
|
"github.com/docker/notary/tuf/signed"
|
|
"github.com/stretchr/testify/assert"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
type rpcHealthCheck func(
|
|
context.Context, *pb.Void, ...grpc.CallOption) (*pb.HealthStatus, error)
|
|
|
|
type StubKeyManagementClient struct {
|
|
pb.KeyManagementClient
|
|
healthCheck rpcHealthCheck
|
|
}
|
|
|
|
func (c StubKeyManagementClient) CheckHealth(x context.Context,
|
|
v *pb.Void, o ...grpc.CallOption) (*pb.HealthStatus, error) {
|
|
return c.healthCheck(x, v, o...)
|
|
}
|
|
|
|
type StubGRPCConnection struct {
|
|
fakeConnStatus grpc.ConnectivityState
|
|
}
|
|
|
|
func (c StubGRPCConnection) State() grpc.ConnectivityState {
|
|
return c.fakeConnStatus
|
|
}
|
|
|
|
func stubHealthFunction(t *testing.T, status map[string]string, err error) rpcHealthCheck {
|
|
return func(ctx context.Context, v *pb.Void, o ...grpc.CallOption) (*pb.HealthStatus, error) {
|
|
_, withDeadline := ctx.Deadline()
|
|
assert.True(t, withDeadline)
|
|
|
|
return &pb.HealthStatus{Status: status}, err
|
|
}
|
|
}
|
|
|
|
func makeSigner(kmFunc rpcHealthCheck, conn StubGRPCConnection) NotarySigner {
|
|
return NotarySigner{
|
|
StubKeyManagementClient{
|
|
pb.NewKeyManagementClient(nil),
|
|
kmFunc,
|
|
},
|
|
pb.NewSignerClient(nil),
|
|
conn,
|
|
}
|
|
}
|
|
|
|
// CheckHealth does not succeed if the KM server is unhealthy
|
|
func TestHealthCheckKMUnhealthy(t *testing.T) {
|
|
signer := makeSigner(
|
|
stubHealthFunction(t, map[string]string{"health": "not good"}, nil),
|
|
StubGRPCConnection{})
|
|
assert.Error(t, signer.CheckHealth(1*time.Second))
|
|
}
|
|
|
|
// CheckHealth does not succeed if the health check to the KM server errors
|
|
func TestHealthCheckKMError(t *testing.T) {
|
|
signer := makeSigner(
|
|
stubHealthFunction(t, nil, errors.New("Something's wrong")),
|
|
StubGRPCConnection{})
|
|
assert.Error(t, signer.CheckHealth(1*time.Second))
|
|
}
|
|
|
|
// CheckHealth does not succeed if the health check to the KM server times out
|
|
func TestHealthCheckKMTimeout(t *testing.T) {
|
|
signer := makeSigner(
|
|
stubHealthFunction(t, nil, grpc.Errorf(codes.DeadlineExceeded, "")),
|
|
StubGRPCConnection{})
|
|
err := signer.CheckHealth(1 * time.Second)
|
|
assert.Error(t, err)
|
|
assert.True(t, strings.Contains(err.Error(), "Timed out"))
|
|
}
|
|
|
|
// CheckHealth succeeds if KM is healthy and reachable.
|
|
func TestHealthCheckKMHealthy(t *testing.T) {
|
|
signer := makeSigner(
|
|
stubHealthFunction(t, make(map[string]string), nil),
|
|
StubGRPCConnection{})
|
|
assert.NoError(t, signer.CheckHealth(1*time.Second))
|
|
}
|
|
|
|
// CheckHealth fails immediately if not connected to the server.
|
|
func TestHealthCheckConnectionDied(t *testing.T) {
|
|
signer := makeSigner(
|
|
stubHealthFunction(t, make(map[string]string), nil),
|
|
StubGRPCConnection{grpc.Connecting})
|
|
assert.Error(t, signer.CheckHealth(1*time.Second))
|
|
}
|
|
|
|
var ret = passphrase.ConstantRetriever("pass")
|
|
|
|
func TestGetPrivateKeyIfNoKey(t *testing.T) {
|
|
signer := setUpSigner(t, trustmanager.NewKeyMemoryStore(ret))
|
|
privKey, _, err := signer.GetPrivateKey("bogus key ID")
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, privKey)
|
|
}
|
|
|
|
func TestGetPrivateKeyAndSignWithExistingKey(t *testing.T) {
|
|
key, err := trustmanager.GenerateECDSAKey(rand.Reader)
|
|
assert.NoError(t, err, "could not generate key")
|
|
|
|
store := trustmanager.NewKeyMemoryStore(ret)
|
|
|
|
err = store.AddKey(key, trustmanager.KeyInfo{Role: data.CanonicalTimestampRole, Gun: "gun"})
|
|
assert.NoError(t, err, "could not add key to store")
|
|
|
|
signer := setUpSigner(t, store)
|
|
|
|
privKey, _, err := signer.GetPrivateKey(key.ID())
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, privKey)
|
|
|
|
msg := []byte("message!")
|
|
sig, err := privKey.Sign(rand.Reader, msg, nil)
|
|
assert.NoError(t, err)
|
|
|
|
err = signed.Verifiers[data.ECDSASignature].Verify(
|
|
data.PublicKeyFromPrivate(key), sig, msg)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
type StubClientFromServers struct {
|
|
api.KeyManagementServer
|
|
api.SignerServer
|
|
}
|
|
|
|
func (c *StubClientFromServers) CreateKey(ctx context.Context,
|
|
algorithm *pb.Algorithm, _ ...grpc.CallOption) (*pb.PublicKey, error) {
|
|
return c.KeyManagementServer.CreateKey(ctx, algorithm)
|
|
}
|
|
|
|
func (c *StubClientFromServers) DeleteKey(ctx context.Context, keyID *pb.KeyID,
|
|
_ ...grpc.CallOption) (*pb.Void, error) {
|
|
return c.KeyManagementServer.DeleteKey(ctx, keyID)
|
|
}
|
|
|
|
func (c *StubClientFromServers) GetKeyInfo(ctx context.Context, keyID *pb.KeyID,
|
|
_ ...grpc.CallOption) (*pb.PublicKey, error) {
|
|
return c.KeyManagementServer.GetKeyInfo(ctx, keyID)
|
|
}
|
|
|
|
func (c *StubClientFromServers) Sign(ctx context.Context,
|
|
sr *pb.SignatureRequest, _ ...grpc.CallOption) (*pb.Signature, error) {
|
|
return c.SignerServer.Sign(ctx, sr)
|
|
}
|
|
|
|
func (c *StubClientFromServers) CheckHealth(ctx context.Context, v *pb.Void,
|
|
_ ...grpc.CallOption) (*pb.HealthStatus, error) {
|
|
return c.KeyManagementServer.CheckHealth(ctx, v)
|
|
}
|
|
|
|
func setUpSigner(t *testing.T, store trustmanager.KeyStore) NotarySigner {
|
|
cryptoService := cryptoservice.NewCryptoService("", store)
|
|
cryptoServices := signer.CryptoServiceIndex{
|
|
data.ED25519Key: cryptoService,
|
|
data.RSAKey: cryptoService,
|
|
data.ECDSAKey: cryptoService,
|
|
}
|
|
|
|
fakeHealth := func() map[string]string { return map[string]string{} }
|
|
|
|
client := StubClientFromServers{
|
|
KeyManagementServer: api.KeyManagementServer{CryptoServices: cryptoServices,
|
|
HealthChecker: fakeHealth},
|
|
SignerServer: api.SignerServer{CryptoServices: cryptoServices,
|
|
HealthChecker: fakeHealth},
|
|
}
|
|
|
|
return NotarySigner{kmClient: &client, sClient: &client}
|
|
}
|