func getCurrentLeader(electionId, namespace string, c client.Interface) (string, *api.Endpoints, error) { endpoints, err := c.Endpoints(namespace).Get(electionId) if err != nil { return "", nil, err } val, found := endpoints.Annotations[leaderelection.LeaderElectionRecordAnnotationKey] if !found { return "", endpoints, nil } electionRecord := leaderelection.LeaderElectionRecord{} if err := json.Unmarshal([]byte(val), &electionRecord); err != nil { return "", nil, err } return electionRecord.HolderIdentity, endpoints, err }
// NewElection creates an election. 'namespace'/'election' should be an existing Kubernetes Service // 'id' is the id if this leader, should be unique. func NewElection(electionId, id, namespace string, ttl time.Duration, callback func(leader string), c client.Interface) (*leaderelection.LeaderElector, error) { _, err := c.Endpoints(namespace).Get(electionId) if err != nil { if errors.IsNotFound(err) { _, err = c.Endpoints(namespace).Create(&api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: electionId, }, }) if err != nil && !errors.IsConflict(err) { return nil, err } } else { return nil, err } } leader, endpoints, err := getCurrentLeader(electionId, namespace, c) if err != nil { return nil, err } callback(leader) broadcaster := record.NewBroadcaster() hostname, err := os.Hostname() if err != nil { return nil, err } recorder := broadcaster.NewRecorder(api.EventSource{ Component: "leader-elector", Host: hostname, }) callbacks := leaderelection.LeaderCallbacks{ OnStartedLeading: func(stop <-chan struct{}) { callback(id) }, OnStoppedLeading: func() { leader, _, err := getCurrentLeader(electionId, namespace, c) if err != nil { glog.Errorf("failed to get leader: %v", err) // empty string means leader is unknown callback("") return } callback(leader) }, } config := leaderelection.LeaderElectionConfig{ Client: c, EventRecorder: recorder, EndpointsMeta: endpoints.ObjectMeta, Identity: id, LeaseDuration: ttl, RenewDeadline: ttl / 2, RetryPeriod: ttl / 4, Callbacks: callbacks, } return leaderelection.NewLeaderElector(config) }