k8s.io/client-go@v0.31.1/examples/leader-election/main.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "context" 21 "flag" 22 "os" 23 "os/signal" 24 "syscall" 25 "time" 26 27 "github.com/google/uuid" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 clientset "k8s.io/client-go/kubernetes" 30 "k8s.io/client-go/rest" 31 "k8s.io/client-go/tools/clientcmd" 32 "k8s.io/client-go/tools/leaderelection" 33 "k8s.io/client-go/tools/leaderelection/resourcelock" 34 "k8s.io/klog/v2" 35 ) 36 37 func buildConfig(kubeconfig string) (*rest.Config, error) { 38 if kubeconfig != "" { 39 cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 40 if err != nil { 41 return nil, err 42 } 43 return cfg, nil 44 } 45 46 cfg, err := rest.InClusterConfig() 47 if err != nil { 48 return nil, err 49 } 50 return cfg, nil 51 } 52 53 func main() { 54 klog.InitFlags(nil) 55 56 var kubeconfig string 57 var leaseLockName string 58 var leaseLockNamespace string 59 var id string 60 61 flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file") 62 flag.StringVar(&id, "id", uuid.New().String(), "the holder identity name") 63 flag.StringVar(&leaseLockName, "lease-lock-name", "", "the lease lock resource name") 64 flag.StringVar(&leaseLockNamespace, "lease-lock-namespace", "", "the lease lock resource namespace") 65 flag.Parse() 66 67 if leaseLockName == "" { 68 klog.Fatal("unable to get lease lock resource name (missing lease-lock-name flag).") 69 } 70 if leaseLockNamespace == "" { 71 klog.Fatal("unable to get lease lock resource namespace (missing lease-lock-namespace flag).") 72 } 73 74 // leader election uses the Kubernetes API by writing to a 75 // lock object, which can be a LeaseLock object (preferred), 76 // a ConfigMap, or an Endpoints (deprecated) object. 77 // Conflicting writes are detected and each client handles those actions 78 // independently. 79 config, err := buildConfig(kubeconfig) 80 if err != nil { 81 klog.Fatal(err) 82 } 83 client := clientset.NewForConfigOrDie(config) 84 85 run := func(ctx context.Context) { 86 // complete your controller loop here 87 klog.Info("Controller loop...") 88 89 select {} 90 } 91 92 // use a Go context so we can tell the leaderelection code when we 93 // want to step down 94 ctx, cancel := context.WithCancel(context.Background()) 95 defer cancel() 96 97 // listen for interrupts or the Linux SIGTERM signal and cancel 98 // our context, which the leader election code will observe and 99 // step down 100 ch := make(chan os.Signal, 1) 101 signal.Notify(ch, os.Interrupt, syscall.SIGTERM) 102 go func() { 103 <-ch 104 klog.Info("Received termination, signaling shutdown") 105 cancel() 106 }() 107 108 // we use the Lease lock type since edits to Leases are less common 109 // and fewer objects in the cluster watch "all Leases". 110 lock := &resourcelock.LeaseLock{ 111 LeaseMeta: metav1.ObjectMeta{ 112 Name: leaseLockName, 113 Namespace: leaseLockNamespace, 114 }, 115 Client: client.CoordinationV1(), 116 LockConfig: resourcelock.ResourceLockConfig{ 117 Identity: id, 118 }, 119 } 120 121 // start the leader election code loop 122 leaderelection.RunOrDie(ctx, leaderelection.LeaderElectionConfig{ 123 Lock: lock, 124 // IMPORTANT: you MUST ensure that any code you have that 125 // is protected by the lease must terminate **before** 126 // you call cancel. Otherwise, you could have a background 127 // loop still running and another process could 128 // get elected before your background loop finished, violating 129 // the stated goal of the lease. 130 ReleaseOnCancel: true, 131 LeaseDuration: 60 * time.Second, 132 RenewDeadline: 15 * time.Second, 133 RetryPeriod: 5 * time.Second, 134 Callbacks: leaderelection.LeaderCallbacks{ 135 OnStartedLeading: func(ctx context.Context) { 136 // we're notified when we start - this is where you would 137 // usually put your code 138 run(ctx) 139 }, 140 OnStoppedLeading: func() { 141 // we can do cleanup here 142 klog.Infof("leader lost: %s", id) 143 os.Exit(0) 144 }, 145 OnNewLeader: func(identity string) { 146 // we're notified when new leader elected 147 if identity == id { 148 // I just got the lock 149 return 150 } 151 klog.Infof("new leader elected: %s", identity) 152 }, 153 }, 154 }) 155 }