sigs.k8s.io/controller-runtime@v0.18.2/pkg/leaderelection/leader_election.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 leaderelection 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 24 "k8s.io/apimachinery/pkg/util/uuid" 25 coordinationv1client "k8s.io/client-go/kubernetes/typed/coordination/v1" 26 corev1client "k8s.io/client-go/kubernetes/typed/core/v1" 27 "k8s.io/client-go/rest" 28 "k8s.io/client-go/tools/leaderelection/resourcelock" 29 30 "sigs.k8s.io/controller-runtime/pkg/recorder" 31 ) 32 33 const inClusterNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" 34 35 // Options provides the required configuration to create a new resource lock. 36 type Options struct { 37 // LeaderElection determines whether or not to use leader election when 38 // starting the manager. 39 LeaderElection bool 40 41 // LeaderElectionResourceLock determines which resource lock to use for leader election, 42 // defaults to "leases". 43 LeaderElectionResourceLock string 44 45 // LeaderElectionNamespace determines the namespace in which the leader 46 // election resource will be created. 47 LeaderElectionNamespace string 48 49 // LeaderElectionID determines the name of the resource that leader election 50 // will use for holding the leader lock. 51 LeaderElectionID string 52 } 53 54 // NewResourceLock creates a new resource lock for use in a leader election loop. 55 func NewResourceLock(config *rest.Config, recorderProvider recorder.Provider, options Options) (resourcelock.Interface, error) { 56 if !options.LeaderElection { 57 return nil, nil 58 } 59 60 // Default resource lock to "leases". The previous default (from v0.7.0 to v0.11.x) was configmapsleases, which was 61 // used to migrate from configmaps to leases. Since the default was "configmapsleases" for over a year, spanning 62 // five minor releases, any actively maintained operators are very likely to have a released version that uses 63 // "configmapsleases". Therefore defaulting to "leases" should be safe. 64 if options.LeaderElectionResourceLock == "" { 65 options.LeaderElectionResourceLock = resourcelock.LeasesResourceLock 66 } 67 68 // LeaderElectionID must be provided to prevent clashes 69 if options.LeaderElectionID == "" { 70 return nil, errors.New("LeaderElectionID must be configured") 71 } 72 73 // Default the namespace (if running in cluster) 74 if options.LeaderElectionNamespace == "" { 75 var err error 76 options.LeaderElectionNamespace, err = getInClusterNamespace() 77 if err != nil { 78 return nil, fmt.Errorf("unable to find leader election namespace: %w", err) 79 } 80 } 81 82 // Leader id, needs to be unique 83 id, err := os.Hostname() 84 if err != nil { 85 return nil, err 86 } 87 id = id + "_" + string(uuid.NewUUID()) 88 89 // Construct clients for leader election 90 rest.AddUserAgent(config, "leader-election") 91 corev1Client, err := corev1client.NewForConfig(config) 92 if err != nil { 93 return nil, err 94 } 95 96 coordinationClient, err := coordinationv1client.NewForConfig(config) 97 if err != nil { 98 return nil, err 99 } 100 101 return resourcelock.New(options.LeaderElectionResourceLock, 102 options.LeaderElectionNamespace, 103 options.LeaderElectionID, 104 corev1Client, 105 coordinationClient, 106 resourcelock.ResourceLockConfig{ 107 Identity: id, 108 EventRecorder: recorderProvider.GetEventRecorderFor(id), 109 }) 110 } 111 112 func getInClusterNamespace() (string, error) { 113 // Check whether the namespace file exists. 114 // If not, we are not running in cluster so can't guess the namespace. 115 if _, err := os.Stat(inClusterNamespacePath); os.IsNotExist(err) { 116 return "", fmt.Errorf("not running in-cluster, please specify LeaderElectionNamespace") 117 } else if err != nil { 118 return "", fmt.Errorf("error checking namespace file: %w", err) 119 } 120 121 // Load the namespace file and return its content 122 namespace, err := os.ReadFile(inClusterNamespacePath) 123 if err != nil { 124 return "", fmt.Errorf("error reading namespace file: %w", err) 125 } 126 return string(namespace), nil 127 }