go.etcd.io/etcd@v3.3.27+incompatible/etcdctl/ctlv3/command/elect_command.go (about) 1 // Copyright 2016 The etcd Authors 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 command 16 17 import ( 18 "context" 19 "errors" 20 "os" 21 "os/signal" 22 "syscall" 23 24 "github.com/coreos/etcd/clientv3" 25 "github.com/coreos/etcd/clientv3/concurrency" 26 27 "github.com/spf13/cobra" 28 ) 29 30 var ( 31 electListen bool 32 ) 33 34 // NewElectCommand returns the cobra command for "elect". 35 func NewElectCommand() *cobra.Command { 36 cmd := &cobra.Command{ 37 Use: "elect <election-name> [proposal]", 38 Short: "Observes and participates in leader election", 39 Run: electCommandFunc, 40 } 41 cmd.Flags().BoolVarP(&electListen, "listen", "l", false, "observation mode") 42 return cmd 43 } 44 45 func electCommandFunc(cmd *cobra.Command, args []string) { 46 if len(args) != 1 && len(args) != 2 { 47 ExitWithError(ExitBadArgs, errors.New("elect takes one election name argument and an optional proposal argument.")) 48 } 49 c := mustClientFromCmd(cmd) 50 51 var err error 52 if len(args) == 1 { 53 if !electListen { 54 ExitWithError(ExitBadArgs, errors.New("no proposal argument but -l not set")) 55 } 56 err = observe(c, args[0]) 57 } else { 58 if electListen { 59 ExitWithError(ExitBadArgs, errors.New("proposal given but -l is set")) 60 } 61 err = campaign(c, args[0], args[1]) 62 } 63 if err != nil { 64 ExitWithError(ExitError, err) 65 } 66 } 67 68 func observe(c *clientv3.Client, election string) error { 69 s, err := concurrency.NewSession(c) 70 if err != nil { 71 return err 72 } 73 e := concurrency.NewElection(s, election) 74 ctx, cancel := context.WithCancel(context.TODO()) 75 76 donec := make(chan struct{}) 77 sigc := make(chan os.Signal, 1) 78 signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) 79 go func() { 80 <-sigc 81 cancel() 82 }() 83 84 go func() { 85 for resp := range e.Observe(ctx) { 86 display.Get(resp) 87 } 88 close(donec) 89 }() 90 91 <-donec 92 93 select { 94 case <-ctx.Done(): 95 default: 96 return errors.New("elect: observer lost") 97 } 98 99 return nil 100 } 101 102 func campaign(c *clientv3.Client, election string, prop string) error { 103 s, err := concurrency.NewSession(c) 104 if err != nil { 105 return err 106 } 107 e := concurrency.NewElection(s, election) 108 ctx, cancel := context.WithCancel(context.TODO()) 109 110 donec := make(chan struct{}) 111 sigc := make(chan os.Signal, 1) 112 signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) 113 go func() { 114 <-sigc 115 cancel() 116 close(donec) 117 }() 118 119 if err = e.Campaign(ctx, prop); err != nil { 120 return err 121 } 122 123 // print key since elected 124 resp, err := c.Get(ctx, e.Key()) 125 if err != nil { 126 return err 127 } 128 display.Get(*resp) 129 130 select { 131 case <-donec: 132 case <-s.Done(): 133 return errors.New("elect: session expired") 134 } 135 136 return e.Resign(context.TODO()) 137 }