github.com/bartle-stripe/trillian@v1.2.1/testonly/integration/logenv.go (about)

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package integration provides test-only code for performing integrated
    16  // tests of Trillian functionality.
    17  package integration
    18  
    19  import (
    20  	"context"
    21  	"database/sql"
    22  	"net"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/golang/glog"
    27  	"github.com/golang/protobuf/proto"
    28  	"google.golang.org/grpc"
    29  
    30  	"github.com/google/trillian"
    31  	"github.com/google/trillian/crypto/keys/der"
    32  	"github.com/google/trillian/crypto/keyspb"
    33  	"github.com/google/trillian/extension"
    34  	"github.com/google/trillian/quota"
    35  	"github.com/google/trillian/server"
    36  	"github.com/google/trillian/server/admin"
    37  	"github.com/google/trillian/server/interceptor"
    38  	"github.com/google/trillian/storage/mysql"
    39  	"github.com/google/trillian/storage/testdb"
    40  	"github.com/google/trillian/util"
    41  
    42  	_ "github.com/go-sql-driver/mysql"                   // Load MySQL driver
    43  	_ "github.com/google/trillian/crypto/keys/der/proto" // Register PrivateKey ProtoHandler
    44  )
    45  
    46  var (
    47  	sequencerWindow = time.Duration(0)
    48  	batchSize       = 50
    49  	// SequencerInterval is the time between runs of the sequencer.
    50  	SequencerInterval = 500 * time.Millisecond
    51  	timeSource        = util.SystemTimeSource{}
    52  )
    53  
    54  // LogEnv is a test environment that contains both a log server and a connection to it.
    55  type LogEnv struct {
    56  	registry        extension.Registry
    57  	pendingTasks    *sync.WaitGroup
    58  	grpcServer      *grpc.Server
    59  	adminServer     *admin.Server
    60  	logServer       *server.TrillianLogRPCServer
    61  	LogOperation    server.LogOperation
    62  	Sequencer       *server.LogOperationManager
    63  	sequencerCancel context.CancelFunc
    64  	ClientConn      *grpc.ClientConn // TODO(gbelvin): Deprecate.
    65  
    66  	Address string
    67  	Log     trillian.TrillianLogClient
    68  	Admin   trillian.TrillianAdminClient
    69  	DB      *sql.DB
    70  }
    71  
    72  // NewLogEnv creates a fresh DB, log server, and client. The numSequencers parameter
    73  // indicates how many sequencers to run in parallel; if numSequencers is zero a
    74  // manually-controlled test sequencer is used.
    75  // TODO(codingllama): Remove 3rd parameter (need to coordinate with
    76  // github.com/google/certificate-transparency-go)
    77  func NewLogEnv(ctx context.Context, numSequencers int, _ string) (*LogEnv, error) {
    78  	return NewLogEnvWithGRPCOptions(ctx, numSequencers, nil, nil)
    79  }
    80  
    81  // NewLogEnvWithGRPCOptions works the same way as NewLogEnv, but allows callers to also set additional grpc.ServerOption and grpc.DialOption values.
    82  func NewLogEnvWithGRPCOptions(ctx context.Context, numSequencers int, serverOpts []grpc.ServerOption, clientOpts []grpc.DialOption) (*LogEnv, error) {
    83  	db, err := testdb.NewTrillianDB(ctx)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	registry := extension.Registry{
    89  		AdminStorage: mysql.NewAdminStorage(db),
    90  		LogStorage:   mysql.NewLogStorage(db, nil),
    91  		QuotaManager: quota.Noop(),
    92  		NewKeyProto: func(ctx context.Context, spec *keyspb.Specification) (proto.Message, error) {
    93  			return der.NewProtoFromSpec(spec)
    94  		},
    95  	}
    96  
    97  	ret, err := NewLogEnvWithRegistryAndGRPCOptions(ctx, numSequencers, registry, serverOpts, clientOpts)
    98  	if err != nil {
    99  		db.Close()
   100  		return nil, err
   101  	}
   102  	ret.DB = db
   103  	return ret, nil
   104  }
   105  
   106  // NewLogEnvWithRegistry uses the passed in Registry to create a log server,
   107  // and client. The numSequencers parameter indicates how many sequencers to
   108  // run in parallel; if numSequencers is zero a manually-controlled test
   109  // sequencer is used.
   110  func NewLogEnvWithRegistry(ctx context.Context, numSequencers int, registry extension.Registry) (*LogEnv, error) {
   111  	return NewLogEnvWithRegistryAndGRPCOptions(ctx, numSequencers, registry, nil, nil)
   112  }
   113  
   114  // NewLogEnvWithRegistryAndGRPCOptions works the same way as NewLogEnv, but allows callers to also set additional grpc.ServerOption and grpc.DialOption values.
   115  func NewLogEnvWithRegistryAndGRPCOptions(ctx context.Context, numSequencers int, registry extension.Registry, serverOpts []grpc.ServerOption, clientOpts []grpc.DialOption) (*LogEnv, error) {
   116  	// Create the GRPC Server.
   117  	serverOpts = append(serverOpts, grpc.UnaryInterceptor(interceptor.ErrorWrapper))
   118  	grpcServer := grpc.NewServer(serverOpts...)
   119  
   120  	// Setup the Admin Server.
   121  	adminServer := admin.New(registry, nil)
   122  	trillian.RegisterTrillianAdminServer(grpcServer, adminServer)
   123  
   124  	// Setup the Log Server.
   125  	logServer := server.NewTrillianLogRPCServer(registry, timeSource)
   126  	trillian.RegisterTrillianLogServer(grpcServer, logServer)
   127  
   128  	// Create Sequencer.
   129  	sequencerManager := server.NewSequencerManager(registry, sequencerWindow)
   130  	var wg sync.WaitGroup
   131  	var sequencerTask *server.LogOperationManager
   132  	ctx, cancel := context.WithCancel(ctx)
   133  	info := server.LogOperationInfo{
   134  		Registry:    registry,
   135  		BatchSize:   batchSize,
   136  		NumWorkers:  numSequencers,
   137  		RunInterval: SequencerInterval,
   138  		TimeSource:  timeSource,
   139  	}
   140  	// Start a live sequencer in a goroutine.
   141  	sequencerTask = server.NewLogOperationManager(info, sequencerManager)
   142  	wg.Add(1)
   143  	go func(wg *sync.WaitGroup, om *server.LogOperationManager) {
   144  		defer wg.Done()
   145  		om.OperationLoop(ctx)
   146  	}(&wg, sequencerTask)
   147  
   148  	// Listen and start server.
   149  	addr, lis, err := listen()
   150  	if err != nil {
   151  		cancel()
   152  		return nil, err
   153  	}
   154  	wg.Add(1)
   155  	go func(wg *sync.WaitGroup, grpcServer *grpc.Server, lis net.Listener) {
   156  		defer wg.Done()
   157  		if err := grpcServer.Serve(lis); err != nil {
   158  			glog.Errorf("gRPC server stopped: %v", err)
   159  			glog.Flush()
   160  		}
   161  	}(&wg, grpcServer, lis)
   162  
   163  	// Connect to the server.
   164  	if clientOpts == nil {
   165  		clientOpts = []grpc.DialOption{grpc.WithInsecure()}
   166  	}
   167  
   168  	cc, err := grpc.Dial(addr, clientOpts...)
   169  	if err != nil {
   170  		cancel()
   171  		return nil, err
   172  	}
   173  
   174  	return &LogEnv{
   175  		registry:        registry,
   176  		pendingTasks:    &wg,
   177  		grpcServer:      grpcServer,
   178  		adminServer:     adminServer,
   179  		logServer:       logServer,
   180  		Address:         addr,
   181  		ClientConn:      cc,
   182  		Log:             trillian.NewTrillianLogClient(cc),
   183  		Admin:           trillian.NewTrillianAdminClient(cc),
   184  		LogOperation:    sequencerManager,
   185  		Sequencer:       sequencerTask,
   186  		sequencerCancel: cancel,
   187  	}, nil
   188  }
   189  
   190  // Close shuts down the server.
   191  func (env *LogEnv) Close() {
   192  	if env.sequencerCancel != nil {
   193  		env.sequencerCancel()
   194  	}
   195  	env.ClientConn.Close()
   196  	env.grpcServer.GracefulStop()
   197  	env.pendingTasks.Wait()
   198  	if env.DB != nil {
   199  		env.DB.Close()
   200  	}
   201  }