2019-01-12 04:58:27 +00:00
|
|
|
/*
|
|
|
|
Copyright 2016 The Kubernetes Authors.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package resourcelock
|
|
|
|
|
|
|
|
import (
|
2020-03-26 21:07:15 +00:00
|
|
|
"context"
|
2019-01-12 04:58:27 +00:00
|
|
|
"fmt"
|
2020-12-01 01:06:26 +00:00
|
|
|
clientset "k8s.io/client-go/kubernetes"
|
|
|
|
restclient "k8s.io/client-go/rest"
|
|
|
|
"time"
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2019-04-07 17:07:55 +00:00
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1"
|
2019-01-12 04:58:27 +00:00
|
|
|
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
LeaderElectionRecordAnnotationKey = "control-plane.alpha.kubernetes.io/leader"
|
|
|
|
EndpointsResourceLock = "endpoints"
|
|
|
|
ConfigMapsResourceLock = "configmaps"
|
2019-04-07 17:07:55 +00:00
|
|
|
LeasesResourceLock = "leases"
|
2019-12-12 01:27:03 +00:00
|
|
|
EndpointsLeasesResourceLock = "endpointsleases"
|
|
|
|
ConfigMapsLeasesResourceLock = "configmapsleases"
|
2019-01-12 04:58:27 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// LeaderElectionRecord is the record that is stored in the leader election annotation.
|
|
|
|
// This information should be used for observational purposes only and could be replaced
|
|
|
|
// with a random string (e.g. UUID) with only slight modification of this code.
|
|
|
|
// TODO(mikedanese): this should potentially be versioned
|
|
|
|
type LeaderElectionRecord struct {
|
2019-04-07 17:07:55 +00:00
|
|
|
// HolderIdentity is the ID that owns the lease. If empty, no one owns this lease and
|
|
|
|
// all callers may acquire. Versions of this library prior to Kubernetes 1.14 will not
|
|
|
|
// attempt to acquire leases with empty identities and will wait for the full lease
|
|
|
|
// interval to expire before attempting to reacquire. This value is set to empty when
|
|
|
|
// a client voluntarily steps down.
|
2019-01-12 04:58:27 +00:00
|
|
|
HolderIdentity string `json:"holderIdentity"`
|
|
|
|
LeaseDurationSeconds int `json:"leaseDurationSeconds"`
|
|
|
|
AcquireTime metav1.Time `json:"acquireTime"`
|
|
|
|
RenewTime metav1.Time `json:"renewTime"`
|
|
|
|
LeaderTransitions int `json:"leaderTransitions"`
|
|
|
|
}
|
|
|
|
|
2019-04-07 17:07:55 +00:00
|
|
|
// EventRecorder records a change in the ResourceLock.
|
|
|
|
type EventRecorder interface {
|
|
|
|
Eventf(obj runtime.Object, eventType, reason, message string, args ...interface{})
|
|
|
|
}
|
|
|
|
|
2019-01-12 04:58:27 +00:00
|
|
|
// ResourceLockConfig common data that exists across different
|
|
|
|
// resource locks
|
|
|
|
type ResourceLockConfig struct {
|
2019-04-07 17:07:55 +00:00
|
|
|
// Identity is the unique string identifying a lease holder across
|
|
|
|
// all participants in an election.
|
|
|
|
Identity string
|
|
|
|
// EventRecorder is optional.
|
|
|
|
EventRecorder EventRecorder
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Interface offers a common interface for locking on arbitrary
|
|
|
|
// resources used in leader election. The Interface is used
|
|
|
|
// to hide the details on specific implementations in order to allow
|
|
|
|
// them to change over time. This interface is strictly for use
|
|
|
|
// by the leaderelection code.
|
|
|
|
type Interface interface {
|
|
|
|
// Get returns the LeaderElectionRecord
|
2020-03-26 21:07:15 +00:00
|
|
|
Get(ctx context.Context) (*LeaderElectionRecord, []byte, error)
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
// Create attempts to create a LeaderElectionRecord
|
2020-03-26 21:07:15 +00:00
|
|
|
Create(ctx context.Context, ler LeaderElectionRecord) error
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
// Update will update and existing LeaderElectionRecord
|
2020-03-26 21:07:15 +00:00
|
|
|
Update(ctx context.Context, ler LeaderElectionRecord) error
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
// RecordEvent is used to record events
|
|
|
|
RecordEvent(string)
|
|
|
|
|
|
|
|
// Identity will return the locks Identity
|
|
|
|
Identity() string
|
|
|
|
|
|
|
|
// Describe is used to convert details on current resource lock
|
|
|
|
// into a string
|
|
|
|
Describe() string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Manufacture will create a lock of a given type according to the input parameters
|
2019-04-07 17:07:55 +00:00
|
|
|
func New(lockType string, ns string, name string, coreClient corev1.CoreV1Interface, coordinationClient coordinationv1.CoordinationV1Interface, rlc ResourceLockConfig) (Interface, error) {
|
2019-12-12 01:27:03 +00:00
|
|
|
endpointsLock := &EndpointsLock{
|
|
|
|
EndpointsMeta: metav1.ObjectMeta{
|
|
|
|
Namespace: ns,
|
|
|
|
Name: name,
|
|
|
|
},
|
|
|
|
Client: coreClient,
|
|
|
|
LockConfig: rlc,
|
|
|
|
}
|
|
|
|
configmapLock := &ConfigMapLock{
|
|
|
|
ConfigMapMeta: metav1.ObjectMeta{
|
|
|
|
Namespace: ns,
|
|
|
|
Name: name,
|
|
|
|
},
|
|
|
|
Client: coreClient,
|
|
|
|
LockConfig: rlc,
|
|
|
|
}
|
|
|
|
leaseLock := &LeaseLock{
|
|
|
|
LeaseMeta: metav1.ObjectMeta{
|
|
|
|
Namespace: ns,
|
|
|
|
Name: name,
|
|
|
|
},
|
|
|
|
Client: coordinationClient,
|
|
|
|
LockConfig: rlc,
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
switch lockType {
|
|
|
|
case EndpointsResourceLock:
|
2019-12-12 01:27:03 +00:00
|
|
|
return endpointsLock, nil
|
2019-01-12 04:58:27 +00:00
|
|
|
case ConfigMapsResourceLock:
|
2019-12-12 01:27:03 +00:00
|
|
|
return configmapLock, nil
|
2019-04-07 17:07:55 +00:00
|
|
|
case LeasesResourceLock:
|
2019-12-12 01:27:03 +00:00
|
|
|
return leaseLock, nil
|
|
|
|
case EndpointsLeasesResourceLock:
|
|
|
|
return &MultiLock{
|
|
|
|
Primary: endpointsLock,
|
|
|
|
Secondary: leaseLock,
|
|
|
|
}, nil
|
|
|
|
case ConfigMapsLeasesResourceLock:
|
|
|
|
return &MultiLock{
|
|
|
|
Primary: configmapLock,
|
|
|
|
Secondary: leaseLock,
|
2019-01-12 04:58:27 +00:00
|
|
|
}, nil
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("Invalid lock-type %s", lockType)
|
|
|
|
}
|
|
|
|
}
|
2020-12-01 01:06:26 +00:00
|
|
|
|
|
|
|
// NewFromKubeconfig will create a lock of a given type according to the input parameters.
|
2021-03-18 22:40:29 +00:00
|
|
|
// Timeout set for a client used to contact to Kubernetes should be lower than
|
|
|
|
// RenewDeadline to keep a single hung request from forcing a leader loss.
|
|
|
|
// Setting it to max(time.Second, RenewDeadline/2) as a reasonable heuristic.
|
2020-12-01 01:06:26 +00:00
|
|
|
func NewFromKubeconfig(lockType string, ns string, name string, rlc ResourceLockConfig, kubeconfig *restclient.Config, renewDeadline time.Duration) (Interface, error) {
|
|
|
|
// shallow copy, do not modify the kubeconfig
|
|
|
|
config := *kubeconfig
|
2021-03-18 22:40:29 +00:00
|
|
|
timeout := renewDeadline / 2
|
2020-12-01 01:06:26 +00:00
|
|
|
if timeout < time.Second {
|
|
|
|
timeout = time.Second
|
|
|
|
}
|
|
|
|
config.Timeout = timeout
|
|
|
|
leaderElectionClient := clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "leader-election"))
|
|
|
|
return New(lockType, ns, name, leaderElectionClient.CoreV1(), leaderElectionClient.CoordinationV1(), rlc)
|
|
|
|
}
|