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

     1  /*
     2  Copyright 2017 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  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
    28  )
    29  
    30  // TODO: This is almost a exact replica of Endpoints lock.
    31  // going forwards as we self host more and more components
    32  // and use ConfigMaps as the means to pass that configuration
    33  // data we will likely move to deprecate the Endpoints lock.
    34  
    35  type ConfigMapLock struct {
    36  	// ConfigMapMeta should contain a Name and a Namespace of a
    37  	// ConfigMapMeta object that the LeaderElector will attempt to lead.
    38  	ConfigMapMeta metav1.ObjectMeta
    39  	Client        corev1client.ConfigMapsGetter
    40  	LockConfig    ResourceLockConfig
    41  	cm            *v1.ConfigMap
    42  }
    43  
    44  // Get returns the election record from a ConfigMap Annotation
    45  func (cml *ConfigMapLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) {
    46  	var record LeaderElectionRecord
    47  	var err error
    48  	cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Get(ctx, cml.ConfigMapMeta.Name, metav1.GetOptions{})
    49  	if err != nil {
    50  		return nil, nil, err
    51  	}
    52  	if cml.cm.Annotations == nil {
    53  		cml.cm.Annotations = make(map[string]string)
    54  	}
    55  	recordStr, found := cml.cm.Annotations[LeaderElectionRecordAnnotationKey]
    56  	recordBytes := []byte(recordStr)
    57  	if found {
    58  		if err := json.Unmarshal(recordBytes, &record); err != nil {
    59  			return nil, nil, err
    60  		}
    61  	}
    62  	return &record, recordBytes, nil
    63  }
    64  
    65  // Create attempts to create a LeaderElectionRecord annotation
    66  func (cml *ConfigMapLock) Create(ctx context.Context, ler LeaderElectionRecord) error {
    67  	recordBytes, err := json.Marshal(ler)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Create(ctx, &v1.ConfigMap{
    72  		ObjectMeta: metav1.ObjectMeta{
    73  			Name:      cml.ConfigMapMeta.Name,
    74  			Namespace: cml.ConfigMapMeta.Namespace,
    75  			Annotations: map[string]string{
    76  				LeaderElectionRecordAnnotationKey: string(recordBytes),
    77  			},
    78  		},
    79  	}, metav1.CreateOptions{})
    80  	return err
    81  }
    82  
    83  // Update will update an existing annotation on a given resource.
    84  func (cml *ConfigMapLock) Update(ctx context.Context, ler LeaderElectionRecord) error {
    85  	if cml.cm == nil {
    86  		return errors.New("configmap not initialized, call get or create first")
    87  	}
    88  	recordBytes, err := json.Marshal(ler)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	if cml.cm.Annotations == nil {
    93  		cml.cm.Annotations = make(map[string]string)
    94  	}
    95  	cml.cm.Annotations[LeaderElectionRecordAnnotationKey] = string(recordBytes)
    96  	cm, err := cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Update(ctx, cml.cm, metav1.UpdateOptions{})
    97  	if err != nil {
    98  		return err
    99  	}
   100  	cml.cm = cm
   101  	return nil
   102  }
   103  
   104  // RecordEvent in leader election while adding meta-data
   105  func (cml *ConfigMapLock) RecordEvent(s string) {
   106  	if cml.LockConfig.EventRecorder == nil {
   107  		return
   108  	}
   109  	events := fmt.Sprintf("%v %v", cml.LockConfig.Identity, s)
   110  	cml.LockConfig.EventRecorder.Eventf(&v1.ConfigMap{ObjectMeta: cml.cm.ObjectMeta}, v1.EventTypeNormal, "LeaderElection", events)
   111  }
   112  
   113  // Describe is used to convert details on current resource lock
   114  // into a string
   115  func (cml *ConfigMapLock) Describe() string {
   116  	return fmt.Sprintf("%v/%v", cml.ConfigMapMeta.Namespace, cml.ConfigMapMeta.Name)
   117  }
   118  
   119  // Identity returns the Identity of the lock
   120  func (cml *ConfigMapLock) Identity() string {
   121  	return cml.LockConfig.Identity
   122  }
   123  
   124  // Identity returns the Identity of the lock
   125  func (cml *ConfigMapLock) Key() string {
   126  	return cml.LockConfig.Key
   127  }