1 /* 2 Copyright 2016 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 resourcelock 18 19 import ( 20 "context" 21 "fmt" 22 clientset "k8s.io/client-go/kubernetes" 23 restclient "k8s.io/client-go/rest" 24 "time" 25 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1" 29 corev1 "k8s.io/client-go/kubernetes/typed/core/v1" 30 ) 31 32 const ( 33 LeaderElectionRecordAnnotationKey = "control-plane.alpha.kubernetes.io/leader" 34 endpointsResourceLock = "endpoints" 35 configMapsResourceLock = "configmaps" 36 LeasesResourceLock = "leases" 37 // When using endpointsLeasesResourceLock, you need to ensure that 38 // API Priority & Fairness is configured with non-default flow-schema 39 // that will catch the necessary operations on leader-election related 40 // endpoint objects. 41 // 42 // The example of such flow scheme could look like this: 43 // apiVersion: flowcontrol.apiserver.k8s.io/v1beta2 44 // kind: FlowSchema 45 // metadata: 46 // name: my-leader-election 47 // spec: 48 // distinguisherMethod: 49 // type: ByUser 50 // matchingPrecedence: 200 51 // priorityLevelConfiguration: 52 // name: leader-election # reference the <leader-election> PL 53 // rules: 54 // - resourceRules: 55 // - apiGroups: 56 // - "" 57 // namespaces: 58 // - '*' 59 // resources: 60 // - endpoints 61 // verbs: 62 // - get 63 // - create 64 // - update 65 // subjects: 66 // - kind: ServiceAccount 67 // serviceAccount: 68 // name: '*' 69 // namespace: kube-system 70 endpointsLeasesResourceLock = "endpointsleases" 71 // When using configMapsLeasesResourceLock, you need to ensure that 72 // API Priority & Fairness is configured with non-default flow-schema 73 // that will catch the necessary operations on leader-election related 74 // configmap objects. 75 // 76 // The example of such flow scheme could look like this: 77 // apiVersion: flowcontrol.apiserver.k8s.io/v1beta2 78 // kind: FlowSchema 79 // metadata: 80 // name: my-leader-election 81 // spec: 82 // distinguisherMethod: 83 // type: ByUser 84 // matchingPrecedence: 200 85 // priorityLevelConfiguration: 86 // name: leader-election # reference the <leader-election> PL 87 // rules: 88 // - resourceRules: 89 // - apiGroups: 90 // - "" 91 // namespaces: 92 // - '*' 93 // resources: 94 // - configmaps 95 // verbs: 96 // - get 97 // - create 98 // - update 99 // subjects: 100 // - kind: ServiceAccount 101 // serviceAccount: 102 // name: '*' 103 // namespace: kube-system 104 configMapsLeasesResourceLock = "configmapsleases" 105 ) 106 107 // LeaderElectionRecord is the record that is stored in the leader election annotation. 108 // This information should be used for observational purposes only and could be replaced 109 // with a random string (e.g. UUID) with only slight modification of this code. 110 // TODO(mikedanese): this should potentially be versioned 111 type LeaderElectionRecord struct { 112 // HolderIdentity is the ID that owns the lease. If empty, no one owns this lease and 113 // all callers may acquire. Versions of this library prior to Kubernetes 1.14 will not 114 // attempt to acquire leases with empty identities and will wait for the full lease 115 // interval to expire before attempting to reacquire. This value is set to empty when 116 // a client voluntarily steps down. 117 HolderIdentity string `json:"holderIdentity"` 118 LeaseDurationSeconds int `json:"leaseDurationSeconds"` 119 AcquireTime metav1.Time `json:"acquireTime"` 120 RenewTime metav1.Time `json:"renewTime"` 121 LeaderTransitions int `json:"leaderTransitions"` 122 } 123 124 // EventRecorder records a change in the ResourceLock. 125 type EventRecorder interface { 126 Eventf(obj runtime.Object, eventType, reason, message string, args ...interface{}) 127 } 128 129 // ResourceLockConfig common data that exists across different 130 // resource locks 131 type ResourceLockConfig struct { 132 // Identity is the unique string identifying a lease holder across 133 // all participants in an election. 134 Identity string 135 // EventRecorder is optional. 136 EventRecorder EventRecorder 137 } 138 139 // Interface offers a common interface for locking on arbitrary 140 // resources used in leader election. The Interface is used 141 // to hide the details on specific implementations in order to allow 142 // them to change over time. This interface is strictly for use 143 // by the leaderelection code. 144 type Interface interface { 145 // Get returns the LeaderElectionRecord 146 Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) 147 148 // Create attempts to create a LeaderElectionRecord 149 Create(ctx context.Context, ler LeaderElectionRecord) error 150 151 // Update will update and existing LeaderElectionRecord 152 Update(ctx context.Context, ler LeaderElectionRecord) error 153 154 // RecordEvent is used to record events 155 RecordEvent(string) 156 157 // Identity will return the locks Identity 158 Identity() string 159 160 // Describe is used to convert details on current resource lock 161 // into a string 162 Describe() string 163 } 164 165 // Manufacture will create a lock of a given type according to the input parameters 166 func New(lockType string, ns string, name string, coreClient corev1.CoreV1Interface, coordinationClient coordinationv1.CoordinationV1Interface, rlc ResourceLockConfig) (Interface, error) { 167 leaseLock := &LeaseLock{ 168 LeaseMeta: metav1.ObjectMeta{ 169 Namespace: ns, 170 Name: name, 171 }, 172 Client: coordinationClient, 173 LockConfig: rlc, 174 } 175 switch lockType { 176 case endpointsResourceLock: 177 return nil, fmt.Errorf("endpoints lock is removed, migrate to %s (using version v0.27.x)", endpointsLeasesResourceLock) 178 case configMapsResourceLock: 179 return nil, fmt.Errorf("configmaps lock is removed, migrate to %s (using version v0.27.x)", configMapsLeasesResourceLock) 180 case LeasesResourceLock: 181 return leaseLock, nil 182 case endpointsLeasesResourceLock: 183 return nil, fmt.Errorf("endpointsleases lock is removed, migrate to %s", LeasesResourceLock) 184 case configMapsLeasesResourceLock: 185 return nil, fmt.Errorf("configmapsleases lock is removed, migrated to %s", LeasesResourceLock) 186 default: 187 return nil, fmt.Errorf("Invalid lock-type %s", lockType) 188 } 189 } 190 191 // NewFromKubeconfig will create a lock of a given type according to the input parameters. 192 // Timeout set for a client used to contact to Kubernetes should be lower than 193 // RenewDeadline to keep a single hung request from forcing a leader loss. 194 // Setting it to max(time.Second, RenewDeadline/2) as a reasonable heuristic. 195 func NewFromKubeconfig(lockType string, ns string, name string, rlc ResourceLockConfig, kubeconfig *restclient.Config, renewDeadline time.Duration) (Interface, error) { 196 // shallow copy, do not modify the kubeconfig 197 config := *kubeconfig 198 timeout := renewDeadline / 2 199 if timeout < time.Second { 200 timeout = time.Second 201 } 202 config.Timeout = timeout 203 leaderElectionClient := clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "leader-election")) 204 return New(lockType, ns, name, leaderElectionClient.CoreV1(), leaderElectionClient.CoordinationV1(), rlc) 205 } 206