Skip to content

Commit

Permalink
Merge pull request #30 from thrawn01/comma-separate-etcd-endpoints
Browse files Browse the repository at this point in the history
ETCD3_ENDPOINT can now accept comma separated ETCD endpoints
  • Loading branch information
thrawn01 authored Aug 23, 2018
2 parents 981bc8e + 9c014e9 commit 7c220cf
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 31 deletions.
129 changes: 99 additions & 30 deletions etcdutil/README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,115 @@
## ETCD Leader Election
## 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 services join and leave the election. Calling `Stop()` will `Concede()` leadership if
we currently have it.
while services join and leave the election. Calling `Stop()` will `Concede()` leadership if
the service currently has it.

```go

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

var wg holster.WaitGroup

// Start the goroutine and preform the election
leader, _ := election.NewElection("my-service", "", nil)

// 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 leader.IsLeader() {
err := DoThing()
if err != nil {
// Have another instance DoThing(), we can't for some reason
leader.Concede()
func main() {
var wg holster.WaitGroup

hostname, err := os.Hostname()
if err != nil {
fmt.Fprintf(os.Stderr, "while obtaining hostname: %s\n", err)
return
}

// Preform an election called 'my-service' with hostname as the candidate name
leader, _ := etcdutil.NewElection("my-service", hostname, nil)

// 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 leader.IsLeader() {
err := DoThing()
if err != nil {
// Have another instance DoThing(), we can't for some reason
leader.Concede()
}
}
return true
case <-signalChan:
leader.Stop()
return false
}
return true
case <-signalChan:
leader.Stop()
return false
})
wg.Wait()
}
```

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

```go
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 `NewEtcdConfig()`
// 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")

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

// Use cfg to init scroll
// Use cfg to init eventbus
// Use cfg to init leader election
}
```

## NewSecureClient()
Just like `NewEtcdConfig()` but returns a connected etcd client for use by the
rest of the application.

```go
import (
"os"
"fmt"

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

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

// Use client
}
```
3 changes: 2 additions & 1 deletion etcdutil/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/x509"
"io/ioutil"
"os"
"strings"
"time"

etcd "github.com/coreos/etcd/clientv3"
Expand Down Expand Up @@ -99,7 +100,7 @@ func NewEtcdConfig(cfg *etcd.Config) (*etcd.Config, error) {
}

holster.SetDefault(&envEndpoint, os.Getenv("ETCD3_ENDPOINT"), localEtcdEndpoint)
holster.SetDefault(&cfg.Endpoints, []string{envEndpoint})
holster.SetDefault(&cfg.Endpoints, strings.Split(envEndpoint, ","))

// Override here if user REALLY wants this
if cfg.TLS != nil && os.Getenv("ETCD3_SKIP_VERIFY") != "" {
Expand Down

0 comments on commit 7c220cf

Please sign in to comment.