// handleBackup handles a request that intends to do a backup. func handleBackup(c *cli.Context) { srcSnap := path.Join(c.String("data-dir"), "member", "snap") destSnap := path.Join(c.String("backup-dir"), "member", "snap") srcWAL := path.Join(c.String("data-dir"), "member", "wal") destWAL := path.Join(c.String("backup-dir"), "member", "wal") if err := os.MkdirAll(destSnap, 0700); err != nil { log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err) } ss := snap.New(srcSnap) snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { log.Fatal(err) } var walsnap walpb.Snapshot if snapshot != nil { walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term newss := snap.New(destSnap) if err = newss.SaveSnap(*snapshot); err != nil { log.Fatal(err) } } w, err := wal.OpenForRead(srcWAL, walsnap) if err != nil { log.Fatal(err) } defer w.Close() wmetadata, state, ents, err := w.ReadAll() switch err { case nil: case wal.ErrSnapshotNotFound: fmt.Printf("Failed to find the match snapshot record %+v in wal %v.", walsnap, srcWAL) fmt.Printf("etcdctl will add it back. Start auto fixing...") default: log.Fatal(err) } var metadata etcdserverpb.Metadata pbutil.MustUnmarshal(&metadata, wmetadata) idgen := idutil.NewGenerator(0, time.Now()) metadata.NodeID = idgen.Next() metadata.ClusterID = idgen.Next() neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata)) if err != nil { log.Fatal(err) } defer neww.Close() if err := neww.Save(state, ents); err != nil { log.Fatal(err) } if err := neww.SaveSnapshot(walsnap); err != nil { log.Fatal(err) } }
// setCommandFunc executes the "set" command. func setCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("key required") } key := c.Args()[0] value, err := argOrStdin(c.Args(), os.Stdin, 1) if err != nil { return nil, errors.New("value required") } ttl := c.Int("ttl") prevValue := c.String("swap-with-value") prevIndex := c.Int("swap-with-index") if prevValue == "" && prevIndex == 0 { return client.Set(key, value, uint64(ttl)) } return client.CompareAndSwap(key, value, uint64(ttl), prevValue, uint64(prevIndex)) }
// rmCommandFunc executes the "rm" command. func rmCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] recursive := c.Bool("recursive") dir := c.Bool("dir") prevValue := c.String("with-value") prevIndex := c.Int("with-index") // TODO: handle transport timeout resp, err := ki.Delete(context.TODO(), key, &client.DeleteOptions{PrevIndex: uint64(prevIndex), PrevValue: prevValue, Dir: dir, Recursive: recursive}) if err != nil { handleError(ExitServerError, err) } if !resp.Node.Dir { printResponseKey(resp, c.GlobalString("output")) } }
// setCommandFunc executes the "set" command. func setCommandFunc(c *cli.Context, ki client.KeysAPI) { if len(c.Args()) == 0 { handleError(ExitBadArgs, errors.New("key required")) } key := c.Args()[0] value, err := argOrStdin(c.Args(), os.Stdin, 1) if err != nil { handleError(ExitBadArgs, errors.New("value required")) } ttl := c.Int("ttl") prevValue := c.String("swap-with-value") prevIndex := c.Int("swap-with-index") resp, err := ki.Set(context.TODO(), key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevIndex: uint64(prevIndex), PrevValue: prevValue}) if err != nil { handleError(ExitServerError, err) } printResponseKey(resp, c.GlobalString("output")) }
// removeCommandFunc executes the "rm" command. func removeCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) { if len(c.Args()) == 0 { return nil, errors.New("Key required") } key := c.Args()[0] recursive := c.Bool("recursive") dir := c.Bool("dir") // TODO: distinguish with flag is not set and empty flag // the cli pkg need to provide this feature prevValue := c.String("with-value") prevIndex := uint64(c.Int("with-index")) if prevValue != "" || prevIndex != 0 { return client.CompareAndDelete(key, prevValue, prevIndex) } if recursive || !dir { return client.Delete(key, recursive) } return client.DeleteDir(key) }
// handleBackup handles a request that intends to do a backup. func handleBackup(c *cli.Context) { srcSnap := path.Join(c.String("data-dir"), "snap") destSnap := path.Join(c.String("backup-dir"), "snap") srcWAL := path.Join(c.String("data-dir"), "wal") destWAL := path.Join(c.String("backup-dir"), "wal") if err := os.MkdirAll(destSnap, 0700); err != nil { log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err) } ss := snap.New(srcSnap) snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { log.Fatal(err) } var index uint64 if snapshot != nil { index = snapshot.Metadata.Index newss := snap.New(destSnap) if err := newss.SaveSnap(*snapshot); err != nil { log.Fatal(err) } } w, err := wal.OpenNotInUse(srcWAL, index) if err != nil { log.Fatal(err) } defer w.Close() wmetadata, state, ents, err := w.ReadAll() if err != nil { log.Fatal(err) } var metadata etcdserverpb.Metadata pbutil.MustUnmarshal(&metadata, wmetadata) rand.Seed(time.Now().UnixNano()) metadata.NodeID = etcdserver.GenID() metadata.ClusterID = etcdserver.GenID() neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata)) if err != nil { log.Fatal(err) } defer neww.Close() if err := neww.Save(state, ents); err != nil { log.Fatal(err) } }
// handleBackup handles a request that intends to do a backup. func handleBackup(c *cli.Context) { srcSnap := path.Join(c.String("data-dir"), "snap") destSnap := path.Join(c.String("backup-dir"), "snap") srcWAL := path.Join(c.String("data-dir"), "wal") destWAL := path.Join(c.String("backup-dir"), "wal") if err := os.MkdirAll(destSnap, 0700); err != nil { log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err) } ss := snap.New(srcSnap) snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { log.Fatal(err) } var walsnap walpb.Snapshot if snapshot != nil { walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term newss := snap.New(destSnap) if err := newss.SaveSnap(*snapshot); err != nil { log.Fatal(err) } } w, err := wal.OpenNotInUse(srcWAL, walsnap) if err != nil { log.Fatal(err) } defer w.Close() wmetadata, state, ents, err := w.ReadAll() if err != nil { log.Fatal(err) } var metadata etcdserverpb.Metadata pbutil.MustUnmarshal(&metadata, wmetadata) idgen := idutil.NewGenerator(0, time.Now()) metadata.NodeID = idgen.Next() metadata.ClusterID = idgen.Next() neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata)) if err != nil { log.Fatal(err) } defer neww.Close() if err := neww.Save(state, ents); err != nil { log.Fatal(err) } }
func handleImportSnap(c *cli.Context) { d, err := ioutil.ReadFile(c.String("snap")) if err != nil { if c.String("snap") == "" { fmt.Printf("no snapshot file provided (use --snap)\n") } else { fmt.Printf("cannot read snapshot file %s\n", c.String("snap")) } os.Exit(1) } st := store.New() err = st.Recovery(d) wg := &sync.WaitGroup{} setc := make(chan set) concurrent := c.Int("c") fmt.Printf("starting to import snapshot %s with %d clients\n", c.String("snap"), concurrent) for i := 0; i < concurrent; i++ { go runSet(mustNewKeyAPI(c), setc, wg) } all, err := st.Get("/", true, true) if err != nil { handleError(ExitServerError, err) } n := copyKeys(all.Node, setc) hiddens := c.StringSlice("hidden") for _, h := range hiddens { allh, err := st.Get(h, true, true) if err != nil { handleError(ExitServerError, err) } n += copyKeys(allh.Node, setc) } close(setc) wg.Wait() fmt.Printf("finished importing %d keys\n", n) }
func roleGrantRevoke(c *cli.Context, grant bool) { path := c.String("path") if path == "" { fmt.Fprintln(os.Stderr, "No path specified; please use `-path`") os.Exit(1) } if pathutil.CanonicalURLPath(path) != path { fmt.Fprintf(os.Stderr, "Not canonical path; please use `-path=%s`\n", pathutil.CanonicalURLPath(path)) os.Exit(1) } read := c.Bool("read") write := c.Bool("write") rw := c.Bool("readwrite") permcount := 0 for _, v := range []bool{read, write, rw} { if v { permcount++ } } if permcount != 1 { fmt.Fprintln(os.Stderr, "Please specify exactly one of -read, -write or -readwrite") os.Exit(1) } var permType client.PermissionType switch { case read: permType = client.ReadPermission case write: permType = client.WritePermission case rw: permType = client.ReadWritePermission } api, role := mustRoleAPIAndName(c) ctx, cancel := contextWithTotalTimeout(c) defer cancel() currentRole, err := api.GetRole(ctx, role) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } var newRole *client.Role if grant { newRole, err = api.GrantRoleKV(ctx, role, []string{path}, permType) } else { newRole, err = api.RevokeRoleKV(ctx, role, []string{path}, permType) } if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } if reflect.DeepEqual(newRole, currentRole) { if grant { fmt.Printf("Role unchanged; already granted") } else { fmt.Printf("Role unchanged; already revoked") } } fmt.Printf("Role %s updated\n", role) }
func handleImportSnap(c *cli.Context) { d, err := ioutil.ReadFile(c.String("snap")) if err != nil { if c.String("snap") == "" { fmt.Printf("no snapshot file provided (use --snap)\n") } else { fmt.Printf("cannot read snapshot file %s\n", c.String("snap")) } os.Exit(1) } st := store.New() err = st.Recovery(d) if err != nil { fmt.Printf("cannot recover the snapshot file: %v\n", err) os.Exit(1) } endpoints, err := getEndpoints(c) if err != nil { handleError(ExitServerError, err) } tr, err := getTransport(c) if err != nil { handleError(ExitServerError, err) } wg := &sync.WaitGroup{} setc := make(chan set) concurrent := c.Int("c") fmt.Printf("starting to import snapshot %s with %d clients\n", c.String("snap"), concurrent) for i := 0; i < concurrent; i++ { client := etcd.NewClient(endpoints) client.SetTransport(tr) if c.GlobalBool("debug") { go dumpCURL(client) } if ok := client.SyncCluster(); !ok { handleError(ExitBadConnection, errors.New("cannot sync with the cluster using endpoints "+strings.Join(endpoints, ", "))) } wg.Add(1) go runSet(client, setc, wg) } all, err := st.Get("/", true, true) if err != nil { handleError(ExitServerError, err) } n := copyKeys(all.Node, setc) hiddens := c.StringSlice("hidden") for _, h := range hiddens { allh, err := st.Get(h, true, true) if err != nil { handleError(ExitServerError, err) } n += copyKeys(allh.Node, setc) } close(setc) wg.Wait() fmt.Printf("finished importing %d keys\n", n) }
func handleUpgrade(c *cli.Context) { if c.String("old-version") != "1" { fmt.Printf("Do not support upgrade from version %s\n", c.String("old-version")) os.Exit(1) } if c.String("new-version") != "2" { fmt.Printf("Do not support upgrade to version %s\n", c.String("new-version")) os.Exit(1) } tls := transport.TLSInfo{ CAFile: c.String("peer-ca-file"), CertFile: c.String("peer-cert-file"), KeyFile: c.String("peer-key-file"), } t, err := transport.NewTransport(tls) if err != nil { log.Fatal(err) } client := http.Client{Transport: t} resp, err := client.Get(c.String("peer-url") + "/v2/admin/next-internal-version") if err != nil { fmt.Printf("Failed to send upgrade request to %s: %v\n", c.String("peer-url"), err) return } if resp.StatusCode == http.StatusOK { fmt.Println("Cluster will start upgrading from internal version 1 to 2 in 10 seconds.") return } if resp.StatusCode == http.StatusNotFound { fmt.Println("Cluster cannot upgrade to 2: version is not 0.4.7") return } fmt.Printf("Faild to send upgrade request to %s: bad status code %d\n", c.String("cluster-url"), resp.StatusCode) }