k8s.io/client-go@v0.31.1/tools/leaderelection/leasecandidate_test.go (about)

     1  /*
     2  Copyright 2024 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  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	v1 "k8s.io/api/coordination/v1"
    25  	"k8s.io/apimachinery/pkg/api/errors"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/util/wait"
    28  	"k8s.io/client-go/kubernetes/fake"
    29  )
    30  
    31  type testcase struct {
    32  	candidateName, candidateNamespace, leaseName string
    33  	binaryVersion, emulationVersion              string
    34  }
    35  
    36  func TestLeaseCandidateCreation(t *testing.T) {
    37  	tc := testcase{
    38  		candidateName:      "foo",
    39  		candidateNamespace: "default",
    40  		leaseName:          "lease",
    41  		binaryVersion:      "1.30.0",
    42  		emulationVersion:   "1.30.0",
    43  	}
    44  
    45  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
    46  	defer cancel()
    47  
    48  	client := fake.NewSimpleClientset()
    49  	candidate, _, err := NewCandidate(
    50  		client,
    51  		tc.candidateNamespace,
    52  		tc.candidateName,
    53  		tc.leaseName,
    54  		tc.binaryVersion,
    55  		tc.emulationVersion,
    56  		[]v1.CoordinatedLeaseStrategy{v1.OldestEmulationVersion},
    57  	)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	go candidate.Run(ctx)
    63  	err = pollForLease(ctx, tc, client, nil)
    64  	if err != nil {
    65  		t.Fatal(err)
    66  	}
    67  }
    68  
    69  func TestLeaseCandidateAck(t *testing.T) {
    70  	tc := testcase{
    71  		candidateName:      "foo",
    72  		candidateNamespace: "default",
    73  		leaseName:          "lease",
    74  		binaryVersion:      "1.30.0",
    75  		emulationVersion:   "1.30.0",
    76  	}
    77  
    78  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
    79  	defer cancel()
    80  
    81  	client := fake.NewSimpleClientset()
    82  
    83  	candidate, _, err := NewCandidate(
    84  		client,
    85  		tc.candidateNamespace,
    86  		tc.candidateName,
    87  		tc.leaseName,
    88  		tc.binaryVersion,
    89  		tc.emulationVersion,
    90  		[]v1.CoordinatedLeaseStrategy{v1.OldestEmulationVersion},
    91  	)
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  
    96  	go candidate.Run(ctx)
    97  	err = pollForLease(ctx, tc, client, nil)
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	// Update PingTime and verify that the client renews
   103  	ensureAfter := &metav1.MicroTime{Time: time.Now()}
   104  	lc, err := client.CoordinationV1alpha1().LeaseCandidates(tc.candidateNamespace).Get(ctx, tc.candidateName, metav1.GetOptions{})
   105  	if err == nil {
   106  		if lc.Spec.PingTime == nil {
   107  			c := lc.DeepCopy()
   108  			c.Spec.PingTime = &metav1.MicroTime{Time: time.Now()}
   109  			_, err = client.CoordinationV1alpha1().LeaseCandidates(tc.candidateNamespace).Update(ctx, c, metav1.UpdateOptions{})
   110  			if err != nil {
   111  				t.Error(err)
   112  			}
   113  		}
   114  	}
   115  	err = pollForLease(ctx, tc, client, ensureAfter)
   116  	if err != nil {
   117  		t.Fatal(err)
   118  	}
   119  }
   120  
   121  func pollForLease(ctx context.Context, tc testcase, client *fake.Clientset, t *metav1.MicroTime) error {
   122  	return wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 10*time.Second, true, func(ctx context.Context) (done bool, err error) {
   123  		lc, err := client.CoordinationV1alpha1().LeaseCandidates(tc.candidateNamespace).Get(ctx, tc.candidateName, metav1.GetOptions{})
   124  		if err != nil {
   125  			if errors.IsNotFound(err) {
   126  				return false, nil
   127  			}
   128  			return true, err
   129  		}
   130  		if lc.Spec.BinaryVersion == tc.binaryVersion &&
   131  			lc.Spec.EmulationVersion == tc.emulationVersion &&
   132  			lc.Spec.LeaseName == tc.leaseName &&
   133  			lc.Spec.RenewTime != nil {
   134  			// Ensure that if a time is provided, the renewTime occurred after the provided time.
   135  			if t != nil && t.After(lc.Spec.RenewTime.Time) {
   136  				return false, nil
   137  			}
   138  			return true, nil
   139  		}
   140  		return false, nil
   141  	})
   142  }