etcdutil

package
v3.0.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 15, 2019 License: Apache-2.0 Imports: 17 Imported by: 0

README

NewElection()

Use etcd for leader election if you have several instances of a service running in production and you only want one of the service instances to preform a task.

LeaderElection starts a goroutine which performs an election and maintains a leader while candidates join and leave the election. Calling Close() will concede leadership if the service currently has it and will withdraw the candidate from the election.


import (
    "github.com/mailgun/holster"
    "github.com/mailgun/holster/etcdutil"
)

func main() {
    var wg holster.WaitGroup

    client, err := etcdutil.NewClient(nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "while creating etcd client: %s\n", err)
        return
    }
    
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()

    // Start a leader election and attempt to become leader, only returns after
    // determining the current leader.
	election := etcdutil.NewElection(ctx, client, etcdutil.ElectionConfig{
		Election:                "my-service",
		Candidate:               "my-candidate",
		EventObserver: func(e etcdutil.Event) {
			leaderChan <- e
			if e.IsDone {
				close(leaderChan)
			}
		},
		TTL: 10,
	})

    // Handle graceful shutdown
    signalChan := make(chan os.Signal, 1)
    signal.Notify(signalChan, os.Interrupt, os.Kill)

    // Do periodic thing
    tick := time.NewTicker(time.Second * 2)
    wg.Loop(func() bool {
        select {
        case <-tick.C:
            // Are we currently leader?
            if election.IsLeader() {
                err := DoThing()
                if err != nil {
                    // Have another instance run DoThing()
                    // since we can't for some reason.
                    election.Concede()
                }
            }
            return true
        case <-signalChan:
            election.Stop()
            return false
        }
    })
    wg.Wait()
    
    // Or you can pipe events to a channel
    for leader := range leaderChan {
    	fmt.Printf("Leader: %v\n", leader)
    }
}

NewConfig()

Designed to be used in applications that share the same etcd config and wish to reuse the same config throughout the application.

import (
    "os"
    "fmt"

    "github.com/mailgun/holster/etcdutil"
)

func main() {
    // These environment variables provided by the environment,
    // we set them here to only to illustrate how `NewConfig()`
    // uses the environment to create a new etcd config
    os.Setenv("ETCD3_USER", "root")
    os.Setenv("ETCD3_PASSWORD", "rootpw")
    os.Setenv("ETCD3_ENDPOINT", "etcd-n01:2379,etcd-n02:2379,etcd-n03:2379")

    // These default to /etc/mailgun/ssl/localhost/etcd-xxx.pem if the files exist
    os.Setenv("ETCD3_TLS_CERT", "/path/to/etcd-cert.pem")
    os.Setenv("ETCD3_TLS_KEY", "/path/to/etcd-key.pem")
    os.Setenv("ETCD3_CA", "/path/to/etcd-ca.pem")
    
    // Set this to force connecting with TLS, but without cert verification
    os.Setenv("ETCD3_SKIP_VERIFY", "true")

    // Create a new etc config from available environment variables
    cfg, err := etcdutil.NewConfig(nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "while creating etcd config: %s\n", err)
        return
    }
}

NewClient()

Just like NewConfig() but returns a connected etcd client for use by the rest of the application.

import (
    "os"
    "fmt"

    "github.com/mailgun/holster/etcdutil"
)

func main() {
    // Create a new etc client from available environment variables
    client, err := etcdutil.NewClient(nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "while creating etcd client: %s\n", err)
        return
    }

    // Use client
}

Documentation

Index

Constants

View Source
const NoLease = etcd.LeaseID(-1)

Variables

This section is empty.

Functions

func NewClient

func NewClient(cfg *etcd.Config) (*etcd.Client, error)

NewClient creates a new etcd.Client with the specified config where blanks are filled from environment variables by NewConfig.

If the provided config is nil and no environment variables are set, it will return a client connecting without TLS via localhost:2379.

func NewConfig

func NewConfig(cfg *etcd.Config) (*etcd.Config, error)

NewConfig creates a new etcd.Config using environment variables. If an existing config is passed, it will fill in missing configuration using environment variables or defaults if they exists on the local system.

If no environment variables are set, it will return a config set to connect without TLS via localhost:2379.

Types

type AlwaysLeaderMock

type AlwaysLeaderMock struct{}

func (*AlwaysLeaderMock) Close

func (s *AlwaysLeaderMock) Close()

func (*AlwaysLeaderMock) Concede

func (s *AlwaysLeaderMock) Concede() (bool, error)

func (*AlwaysLeaderMock) IsLeader

func (s *AlwaysLeaderMock) IsLeader() bool

type Election

type Election struct {
	// contains filtered or unexported fields
}

func NewElection

func NewElection(ctx context.Context, client *etcd.Client, conf ElectionConfig) (*Election, error)

NewElection creates a new leader election and submits our candidate for leader.

 client, _ := etcdutil.NewClient(nil)

 // Start a leader election and attempt to become leader, only returns after
 // determining the current leader.
 election := etcdutil.NewElection(client, etcdutil.ElectionConfig{
     Election: "presidental",
     Candidate: "donald",
		EventObserver: func(e etcdutil.Event) {
		  	fmt.Printf("Leader Data: %t\n", e.LeaderData)
			if e.IsLeader {
				// Do thing as leader
			}
		},
     TTL: 5,
 })

	// Returns true if we are leader (thread safe)
	if election.IsLeader() {
		// Do periodic thing
	}

 // Concede the election if leader and cancel our candidacy
 // for the election.
 election.Close()

func (*Election) Close

func (e *Election) Close()

Close cancels the election and concedes the election if we are leader

func (*Election) Concede

func (e *Election) Concede() (bool, error)

Concede concedes leadership if we are leader and restarts the campaign returns true. if we are not leader do nothing and return false. If you want to concede leadership and cancel the campaign call Close() instead.

func (*Election) IsLeader

func (e *Election) IsLeader() bool

IsLeader returns true if we are leader

type ElectionConfig

type ElectionConfig struct {
	// Optional function when provided is called every time leadership changes or an error occurs
	EventObserver EventObserver
	// The name of the election (IE: scout, blackbird, etc...)
	Election string
	// The name of this instance (IE: worker-n01, worker-n02, etc...)
	Candidate string
	// Seconds to wait before giving up the election if leader disconnected
	TTL int64
}

type Event

type Event struct {
	// True if our candidate is leader
	IsLeader bool
	// True if the election is shutdown and
	// no further events will follow.
	IsDone bool
	// Holds the current leader key
	LeaderKey string
	// Hold the current leaders data
	LeaderData string
	// If not nil, contains an error encountered
	// while participating in the election.
	Err error
}

type EventObserver

type EventObserver func(Event)

type LeaderElector

type LeaderElector interface {
	IsLeader() bool
	Concede() (bool, error)
	Close()
}

type Session

type Session struct {
	// contains filtered or unexported fields
}

func NewSession

func NewSession(c *etcd.Client, conf SessionConfig) (*Session, error)

NewSession creates a lease and monitors lease keep alive's for connectivity. Once a lease ID is granted SessionConfig.Observer is called with the granted lease. If connectivity is lost with etcd SessionConfig.Observer is called again with -1 (NoLease) as the lease ID. The Session will continue to try to gain another lease, once a new lease is gained SessionConfig.Observer is called again with the new lease id.

func (*Session) Close

func (s *Session) Close()

Close terminates the session shutting down all network operations, then SessionConfig.Observer is called with -1 (NoLease), only returns once the session has closed successfully.

func (*Session) Reset

func (s *Session) Reset(ctx context.Context)

type SessionConfig

type SessionConfig struct {
	TTL      int64
	Observer SessionObserver
}

type SessionObserver

type SessionObserver func(etcd.LeaseID, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL