Skip to content

Instantly share code, notes, and snippets.

@alaypatel07
Created September 4, 2019 18:20
Show Gist options
  • Save alaypatel07/94bd7f1da85c1db17a9e684384de1882 to your computer and use it in GitHub Desktop.
Save alaypatel07/94bd7f1da85c1db17a9e684384de1882 to your computer and use it in GitHub Desktop.
remove member from data directory
package main
import (
"encoding/binary"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
bolt "go.etcd.io/bbolt"
"go.etcd.io/etcd/mvcc/mvccpb"
)
func snapDir(dataDir string) string {
return filepath.Join(dataDir, "member", "snap")
}
func existFileOrDir(name string) bool {
_, err := os.Stat(name)
return err == nil
}
type decoder func(k, v []byte)
var decoders = map[string]decoder{
"key": keyDecoder,
}
type revision struct {
main int64
sub int64
}
func bytesToRev(bytes []byte) revision {
return revision{
main: int64(binary.BigEndian.Uint64(bytes[0:8])),
sub: int64(binary.BigEndian.Uint64(bytes[9:])),
}
}
func keyDecoder(k, v []byte) {
rev := bytesToRev(k)
var kv mvccpb.KeyValue
if err := kv.Unmarshal(v); err != nil {
panic(err)
}
fmt.Printf("rev=%+v, value=[key %q | val %q | created %d | mod %d | ver %d]\n", rev, string(kv.Key), string(kv.Value), kv.CreateRevision, kv.ModRevision, kv.Version)
}
func iterateBucket(dbPath, bucket string, limit uint64, decode bool) (err error) {
db, err := bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 10 * time.Second})
if err != nil {
return fmt.Errorf("failed to open bolt DB %v", err)
}
defer db.Close()
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucket))
if b == nil {
return fmt.Errorf("got nil bucket for %s", bucket)
}
c := b.Cursor()
// iterate in reverse order (use First() and Next() for ascending order)
for k, v := c.Last(); k != nil; k, v = c.Prev() {
// TODO: remove sensitive information
// (https://github.com/etcd-io/etcd/issues/7620)
if dec, ok := decoders[bucket]; decode && ok {
dec(k, v)
} else {
fmt.Printf("key=%q, value=%q\n", k, v)
}
limit--
if limit == 0 {
break
}
}
return nil
})
return err
}
// Find returns the smallest index i at which x == a[i],
// or len(a) if there is no such index.
func Find(a []string, x string) int {
fmt.Println(x)
for i, n := range a {
if x == n {
return i
}
}
return len(a)
}
func removeIDs(dbPath, bucket string, ids []string) error {
db, err := bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 10 * time.Second})
if err != nil {
return fmt.Errorf("failed to open bolt DB %v", err)
}
defer db.Close()
if bucket != "members" {
return fmt.Errorf("can not remove member if this is not a member bucket")
}
tx, err := db.Begin(true)
if err != nil {
log.Printf("unable to open a writable transaction %v", err)
}
c := tx.Bucket([]byte(bucket)).Cursor()
defer tx.Commit()
// iterate in reverse order (use First() and Next() for ascending order)
for k, v := c.First(); k != nil; k, v = c.Next() {
// TODO: remove sensitive information
// (https://github.com/etcd-io/etcd/issues/7620)
index := Find(ids, string(k))
fmt.Printf("Index %v\n", index)
if index >= len(ids) {
fmt.Printf("ignoring key %q: not to be deleted\n", k)
}
if index < len(ids) {
fmt.Printf("deleteing key %v with value %v\n", k, v)
if err := c.Delete(); err != nil {
log.Printf("unable to delete %v: %v", k, err)
}
}
}
return err
}
var dataDir string
var remove_ids string
func main() {
flag.StringVar(&dataDir, "data-dir", "/var/etcd/data", "Enter the filepath to etcd data directory")
flag.StringVar(&remove_ids, "remove-ids", "", "[OPTIONAL] Enter the ids to be removed separated by ','")
flag.Parse()
if !strings.HasSuffix(dataDir, "db") {
dataDir = filepath.Join(snapDir(dataDir), "db")
}
if !existFileOrDir(dataDir) {
log.Fatalf("%q does not exist", dataDir)
}
err := iterateBucket(dataDir, "members", 0, true)
if err != nil {
log.Fatal(err)
}
err = iterateBucket(dataDir, "members_removed", 0, true)
if err != nil {
log.Fatal(err)
}
err = iterateBucket(dataDir, "cluster", 0, true)
if err != nil {
log.Fatal(err)
}
if remove_ids != "" {
ids := strings.Split(remove_ids, ",")
fmt.Printf("ids to be removed %v\n", ids)
err := removeIDs(dataDir, "members", ids)
if err != nil {
log.Println(err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment