istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock/interface.go (about)

     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 k8sresourcelock
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	clientset "k8s.io/client-go/kubernetes"
    27  	coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1"
    28  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    29  	restclient "k8s.io/client-go/rest"
    30  )
    31  
    32  const (
    33  	LeaderElectionRecordAnnotationKey = "control-plane.alpha.kubernetes.io/leader"
    34  	EndpointsResourceLock             = "endpoints"
    35  	ConfigMapsResourceLock            = "configmaps"
    36  	LeasesResourceLock                = "leases"
    37  	EndpointsLeasesResourceLock       = "endpointsleases"
    38  	ConfigMapsLeasesResourceLock      = "configmapsleases"
    39  )
    40  
    41  // LeaderElectionRecord is the record that is stored in the leader election annotation.
    42  // This information should be used for observational purposes only and could be replaced
    43  // with a random string (e.g. UUID) with only slight modification of this code.
    44  // TODO(mikedanese): this should potentially be versioned
    45  type LeaderElectionRecord struct {
    46  	// HolderIdentity is the ID that owns the lease. If empty, no one owns this lease and
    47  	// all callers may acquire. Versions of this library prior to Kubernetes 1.14 will not
    48  	// attempt to acquire leases with empty identities and will wait for the full lease
    49  	// interval to expire before attempting to reacquire. This value is set to empty when
    50  	// a client voluntarily steps down.
    51  	HolderIdentity string `json:"holderIdentity"`
    52  	// HolderKey is the Key of the lease owner. This may be empty if a key is not set.
    53  	HolderKey            string      `json:"holderKey"`
    54  	LeaseDurationSeconds int         `json:"leaseDurationSeconds"`
    55  	AcquireTime          metav1.Time `json:"acquireTime"`
    56  	RenewTime            metav1.Time `json:"renewTime"`
    57  	LeaderTransitions    int         `json:"leaderTransitions"`
    58  }
    59  
    60  // EventRecorder records a change in the ResourceLock.
    61  type EventRecorder interface {
    62  	Eventf(obj runtime.Object, eventType, reason, message string, args ...any)
    63  }
    64  
    65  // ResourceLockConfig common data that exists across different
    66  // resource locks
    67  type ResourceLockConfig struct {
    68  	// Identity is the unique string identifying a lease holder across
    69  	// all participants in an election.
    70  	Identity string
    71  	// Key is a user-defined value used to indicate how high priority this lock
    72  	// have. Other locks may steal the lock from us if they believe their key
    73  	// has a higher priority.
    74  	Key string
    75  	// EventRecorder is optional.
    76  	EventRecorder EventRecorder
    77  }
    78  
    79  // Interface offers a common interface for locking on arbitrary
    80  // resources used in leader election.  The Interface is used
    81  // to hide the details on specific implementations in order to allow
    82  // them to change over time.  This interface is strictly for use
    83  // by the leaderelection code.
    84  type Interface interface {
    85  	// Get returns the LeaderElectionRecord
    86  	Get(ctx context.Context) (*LeaderElectionRecord, []byte, error)
    87  
    88  	// Create attempts to create a LeaderElectionRecord
    89  	Create(ctx context.Context, ler LeaderElectionRecord) error
    90  
    91  	// Update will update and existing LeaderElectionRecord
    92  	Update(ctx context.Context, ler LeaderElectionRecord) error
    93  
    94  	// RecordEvent is used to record events
    95  	RecordEvent(string)
    96  
    97  	// Identity will return the locks Identity
    98  	Identity() string
    99  	Key() string
   100  
   101  	// Describe is used to convert details on current resource lock
   102  	// into a string
   103  	Describe() string
   104  }
   105  
   106  // Manufacture will create a lock of a given type according to the input parameters
   107  // nolint: lll
   108  func New(lockType string, ns string, name string, coreClient corev1.CoreV1Interface, coordinationClient coordinationv1.CoordinationV1Interface, rlc ResourceLockConfig) (Interface, error) {
   109  	endpointsLock := &EndpointsLock{
   110  		EndpointsMeta: metav1.ObjectMeta{
   111  			Namespace: ns,
   112  			Name:      name,
   113  		},
   114  		Client:     coreClient,
   115  		LockConfig: rlc,
   116  	}
   117  	configmapLock := &ConfigMapLock{
   118  		ConfigMapMeta: metav1.ObjectMeta{
   119  			Namespace: ns,
   120  			Name:      name,
   121  		},
   122  		Client:     coreClient,
   123  		LockConfig: rlc,
   124  	}
   125  	leaseLock := &LeaseLock{
   126  		LeaseMeta: metav1.ObjectMeta{
   127  			Namespace: ns,
   128  			Name:      name,
   129  		},
   130  		Client:     coordinationClient,
   131  		LockConfig: rlc,
   132  	}
   133  	switch lockType {
   134  	case EndpointsResourceLock:
   135  		return endpointsLock, nil
   136  	case ConfigMapsResourceLock:
   137  		return configmapLock, nil
   138  	case LeasesResourceLock:
   139  		return leaseLock, nil
   140  	case EndpointsLeasesResourceLock:
   141  		return &MultiLock{
   142  			Primary:   endpointsLock,
   143  			Secondary: leaseLock,
   144  		}, nil
   145  	case ConfigMapsLeasesResourceLock:
   146  		return &MultiLock{
   147  			Primary:   configmapLock,
   148  			Secondary: leaseLock,
   149  		}, nil
   150  	default:
   151  		return nil, fmt.Errorf("invalid lock-type %s", lockType)
   152  	}
   153  }
   154  
   155  // NewFromKubeconfig will create a lock of a given type according to the input parameters.
   156  // Timeout set for a client used to contact to Kubernetes should be lower than
   157  // RenewDeadline to keep a single hung request from forcing a leader loss.
   158  // Setting it to max(time.Second, RenewDeadline/2) as a reasonable heuristic.
   159  // nolint: lll
   160  func NewFromKubeconfig(lockType string, ns string, name string, rlc ResourceLockConfig, kubeconfig *restclient.Config, renewDeadline time.Duration) (Interface, error) {
   161  	// shallow copy, do not modify the kubeconfig
   162  	config := *kubeconfig
   163  	timeout := renewDeadline / 2
   164  	if timeout < time.Second {
   165  		timeout = time.Second
   166  	}
   167  	config.Timeout = timeout
   168  	leaderElectionClient := clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "leader-election"))
   169  	return New(lockType, ns, name, leaderElectionClient.CoreV1(), leaderElectionClient.CoordinationV1(), rlc)
   170  }