github.com/mre-fog/trillianxx@v1.1.2-0.20180615153820-ae375a99d36a/util/etcd/election.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 etcd holds an etcd-specific implementation of the
    16  // util.MasterElection interface.
    17  package etcd
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/coreos/etcd/clientv3"
    25  	"github.com/coreos/etcd/clientv3/concurrency"
    26  	"github.com/golang/glog"
    27  	"github.com/google/trillian/util/election"
    28  )
    29  
    30  // MasterElection is an implementation of util.MasterElection based on etcd.
    31  type MasterElection struct {
    32  	instanceID string
    33  	treeID     int64
    34  	lockFile   string
    35  	client     *clientv3.Client
    36  	session    *concurrency.Session
    37  	election   *concurrency.Election
    38  }
    39  
    40  // Start commences election operation.
    41  func (eme *MasterElection) Start(ctx context.Context) error {
    42  	return nil
    43  }
    44  
    45  // WaitForMastership blocks until the current instance is master.
    46  func (eme *MasterElection) WaitForMastership(ctx context.Context) error {
    47  	return eme.election.Campaign(ctx, eme.instanceID)
    48  }
    49  
    50  // IsMaster returns whether the current instance is the master.
    51  func (eme *MasterElection) IsMaster(ctx context.Context) (bool, error) {
    52  	leader, err := eme.election.Leader(ctx)
    53  	if err != nil {
    54  		return false, err
    55  	}
    56  	return string(leader.Kvs[0].Value) == eme.instanceID, nil
    57  }
    58  
    59  // Resign releases mastership.
    60  func (eme *MasterElection) Resign(ctx context.Context) error {
    61  	return eme.election.Resign(ctx)
    62  }
    63  
    64  // Close terminates election operation.
    65  func (eme *MasterElection) Close(ctx context.Context) error {
    66  	_ = eme.Resign(ctx)
    67  	return eme.session.Close()
    68  }
    69  
    70  // GetCurrentMaster returns the instanceID of the current master, if any.
    71  func (eme *MasterElection) GetCurrentMaster(ctx context.Context) (string, error) {
    72  	leader, err := eme.election.Leader(ctx)
    73  	switch {
    74  	case err == concurrency.ErrElectionNoLeader:
    75  		return "", election.ErrNoMaster
    76  	case err != nil:
    77  		return "", err
    78  	}
    79  	return string(leader.Kvs[0].Value), nil
    80  }
    81  
    82  // ElectionFactory creates etcd.MasterElection instances.
    83  type ElectionFactory struct {
    84  	client     *clientv3.Client
    85  	instanceID string
    86  	lockDir    string
    87  }
    88  
    89  // NewElectionFactory builds an election factory that uses the given
    90  // parameters. The passed in etcd client should remain valid for the lifetime
    91  // of the ElectionFactory.
    92  func NewElectionFactory(instanceID string, client *clientv3.Client, lockDir string) *ElectionFactory {
    93  	return &ElectionFactory{
    94  		client:     client,
    95  		instanceID: instanceID,
    96  		lockDir:    lockDir,
    97  	}
    98  }
    99  
   100  // NewElection creates a specific etcd.MasterElection instance.
   101  func (ef ElectionFactory) NewElection(ctx context.Context, treeID int64) (election.MasterElection, error) {
   102  	session, err := concurrency.NewSession(ef.client)
   103  	if err != nil {
   104  		return nil, fmt.Errorf("failed to create etcd session: %v", err)
   105  	}
   106  	lockFile := fmt.Sprintf("%s/%d", strings.TrimRight(ef.lockDir, "/"), treeID)
   107  	election := concurrency.NewElection(session, lockFile)
   108  
   109  	eme := MasterElection{
   110  		instanceID: ef.instanceID,
   111  		treeID:     treeID,
   112  		lockFile:   lockFile,
   113  		client:     ef.client,
   114  		session:    session,
   115  		election:   election,
   116  	}
   117  	glog.Infof("MasterElection created: %+v", eme)
   118  	return &eme, nil
   119  }