...

Source file src/k8s.io/client-go/examples/leader-election/main.go

Documentation: k8s.io/client-go/examples/leader-election

     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  }
   156  

View as plain text