...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/lease/leaser/leaser_integration_test.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/lease/leaser

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build integration
    16  // +build integration
    17  
    18  package leaser_test
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
    27  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/lease/leaser"
    28  	testreconciler "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller/reconciler"
    29  	testmain "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/main"
    30  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/resourcefixture"
    31  	testrunner "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/runner"
    32  
    33  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    34  	"sigs.k8s.io/controller-runtime/pkg/manager"
    35  )
    36  
    37  var (
    38  	mgr                  manager.Manager
    39  	defaultLeaseDuration = 5 * time.Minute
    40  )
    41  
    42  func TestUnsupportedResourceShouldFail(t *testing.T) {
    43  	shouldRun := func(fixture resourcefixture.ResourceFixture, mgr manager.Manager) bool {
    44  		switch fixture.GVK.Kind {
    45  		case "SourceRepoRepository", // Resource with no labels field
    46  			"DataflowJob": // Resource for which leasing is explicitly disabled (special case).
    47  			return true
    48  		default:
    49  			return false
    50  		}
    51  	}
    52  	testFunc := func(t *testing.T, testContext testrunner.TestContext, systemContext testrunner.SystemContext) {
    53  		leaser := leaser.NewLeaser(systemContext.TFProvider, systemContext.SMLoader, systemContext.Manager.GetClient())
    54  		ok, err := leaser.UnstructuredSupportsLeasing(testContext.CreateUnstruct)
    55  		if err != nil {
    56  			t.Fatalf("error checking for lease support: %v", err)
    57  		}
    58  		if ok {
    59  			t.Fatalf("test should only be run on resources that do not support locking")
    60  		}
    61  		err = leaser.Obtain(context.Background(), testContext.CreateUnstruct, "my-owner-id", defaultLeaseDuration, defaultLeaseDuration)
    62  		if err == nil {
    63  			t.Fatalf("expected error, instead got nil")
    64  		}
    65  		expectedMsg := fmt.Sprintf("gvk '%v' does not support locking", testContext.CreateUnstruct.GroupVersionKind())
    66  		errMsg := err.Error()
    67  		if errMsg != expectedMsg {
    68  			t.Fatalf("unexpected error message: got '%v', want '%v'", errMsg, expectedMsg)
    69  		}
    70  	}
    71  	testrunner.RunAllWithObjectCreated(t, mgr, shouldRun, testFunc)
    72  }
    73  
    74  func TestAll(t *testing.T) {
    75  	shouldRun := func(fixture resourcefixture.ResourceFixture, mgr manager.Manager) bool {
    76  		switch fixture.GVK.Kind {
    77  		case "PubSubTopic":
    78  			return true
    79  		default:
    80  			return false
    81  		}
    82  	}
    83  	testFunc := func(t *testing.T, testContext testrunner.TestContext, systemContext testrunner.SystemContext) {
    84  		// By default, the test framework creates resources with the management conflict policy of 'resource'. If we allow that,
    85  		// then the namespace will have a lease on the resource. That would interfere with the results of this test as we want to
    86  		// obtain and release the lease for our 'owners' that we generate below. For that reason, we set the conflict policy to
    87  		// 'none' and create and reconcile the resource below.
    88  		k8s.SetAnnotation(k8s.ManagementConflictPreventionPolicyFullyQualifiedAnnotation, k8s.ManagementConflictPreventionPolicyNone, testContext.CreateUnstruct)
    89  		if err := systemContext.Manager.GetClient().Create(context.TODO(), testContext.CreateUnstruct); err != nil {
    90  			t.Fatalf("error creating resource: %v", err)
    91  		}
    92  		resourceCleanup := systemContext.Reconciler.BuildCleanupFunc(testContext.CreateUnstruct, testreconciler.CleanupPolicyAlways)
    93  		defer resourceCleanup()
    94  		systemContext.Reconciler.Reconcile(testContext.CreateUnstruct, testreconciler.ExpectedSuccessfulReconcileResultFor(systemContext.Reconciler, testContext.CreateUnstruct), nil)
    95  		leaser := leaser.NewLeaser(systemContext.TFProvider, systemContext.SMLoader, systemContext.Manager.GetClient())
    96  		uniqueId1 := fmt.Sprintf("l1-%v", testContext.UniqueId)
    97  		uniqueId2 := fmt.Sprintf("l2-%v", testContext.UniqueId)
    98  		initialUnstruct := testContext.CreateUnstruct
    99  		testObtainReleaseShouldSucceed(t, initialUnstruct, uniqueId1, leaser)
   100  		testObtainTwiceShouldSucceed(t, initialUnstruct, uniqueId1, leaser)
   101  		testReleaseUnobtainedShouldFail(t, initialUnstruct, uniqueId1, leaser)
   102  		testReleasingExpiredResourceShouldFail(t, initialUnstruct, uniqueId1, leaser)
   103  		testRenewLease(t, initialUnstruct, uniqueId1, leaser)
   104  		testObtainingPreviouslyReleasedResourceShouldSucceed(t, initialUnstruct, uniqueId1, uniqueId2, leaser)
   105  		testObtainingLockedResourceShouldFail(t, initialUnstruct, uniqueId1, uniqueId2, leaser)
   106  		testReleasingLockedResourceShouldFail(t, initialUnstruct, uniqueId1, uniqueId2, leaser)
   107  		testObtainingExpiredLeaseShouldSucceed(t, initialUnstruct, uniqueId1, uniqueId2, leaser)
   108  	}
   109  	testrunner.RunAllWithDependenciesCreatedButNotObject(t, mgr, shouldRun, testFunc)
   110  }
   111  
   112  func testObtainReleaseShouldSucceed(t *testing.T, u *unstructured.Unstructured, uniqueId string, leaser *leaser.Leaser) {
   113  	obtainAssertSuccess(t, u, uniqueId, defaultLeaseDuration, defaultLeaseDuration, leaser)
   114  	releaseAssertSuccess(t, u, uniqueId, leaser)
   115  }
   116  
   117  func testObtainTwiceShouldSucceed(t *testing.T, u *unstructured.Unstructured, uniqueId string, leaser *leaser.Leaser) {
   118  	obtainAssertSuccess(t, u, uniqueId, defaultLeaseDuration, defaultLeaseDuration, leaser)
   119  	obtainAssertSuccess(t, u, uniqueId, defaultLeaseDuration, defaultLeaseDuration, leaser)
   120  	releaseAssertSuccess(t, u, uniqueId, leaser)
   121  }
   122  
   123  func testReleaseUnobtainedShouldFail(t *testing.T, u *unstructured.Unstructured, uniqueId string, leaser *leaser.Leaser) {
   124  	releaseAssertError(t, u, uniqueId, leaser)
   125  }
   126  
   127  func testObtainingPreviouslyReleasedResourceShouldSucceed(t *testing.T, u *unstructured.Unstructured, uniqueId1, uniqueId2 string, leaser *leaser.Leaser) {
   128  	obtainAssertSuccess(t, u, uniqueId1, defaultLeaseDuration, defaultLeaseDuration, leaser)
   129  	releaseAssertSuccess(t, u, uniqueId1, leaser)
   130  	obtainAssertSuccess(t, u, uniqueId2, defaultLeaseDuration, defaultLeaseDuration, leaser)
   131  	releaseAssertSuccess(t, u, uniqueId2, leaser)
   132  }
   133  
   134  func testObtainingLockedResourceShouldFail(t *testing.T, u *unstructured.Unstructured, uniqueId1, uniqueId2 string, leaser *leaser.Leaser) {
   135  	obtainAssertSuccess(t, u, uniqueId1, defaultLeaseDuration, defaultLeaseDuration, leaser)
   136  	obtainAssertError(t, u, uniqueId2, defaultLeaseDuration, defaultLeaseDuration, leaser)
   137  	releaseAssertSuccess(t, u, uniqueId1, leaser)
   138  }
   139  
   140  func testReleasingLockedResourceShouldFail(t *testing.T, u *unstructured.Unstructured, uniqueId1, uniqueId2 string, leaser *leaser.Leaser) {
   141  	obtainAssertSuccess(t, u, uniqueId1, defaultLeaseDuration, defaultLeaseDuration, leaser)
   142  	releaseAssertError(t, u, uniqueId2, leaser)
   143  	releaseAssertSuccess(t, u, uniqueId1, leaser)
   144  }
   145  
   146  func testReleasingExpiredResourceShouldFail(t *testing.T, u *unstructured.Unstructured, uniqueId string, leaser *leaser.Leaser) {
   147  	shortLeaseDuration := 1 * time.Second
   148  	obtainAssertSuccess(t, u, uniqueId, shortLeaseDuration, shortLeaseDuration, leaser)
   149  	time.Sleep(shortLeaseDuration + 1*time.Second)
   150  	releaseAssertError(t, u, uniqueId, leaser)
   151  }
   152  
   153  func testObtainingExpiredLeaseShouldSucceed(t *testing.T, u *unstructured.Unstructured, uniqueId1, uniqueId2 string, leaser *leaser.Leaser) {
   154  	shortLeaseDuration := 10 * time.Second
   155  	obtainAssertSuccess(t, u, uniqueId1, shortLeaseDuration, shortLeaseDuration, leaser)
   156  	obtainAssertError(t, u, uniqueId2, defaultLeaseDuration, defaultLeaseDuration, leaser)
   157  	time.Sleep(shortLeaseDuration)
   158  	obtainAssertSuccess(t, u, uniqueId2, defaultLeaseDuration, defaultLeaseDuration, leaser)
   159  	releaseAssertError(t, u, uniqueId1, leaser)
   160  	releaseAssertSuccess(t, u, uniqueId2, leaser)
   161  }
   162  
   163  func testRenewLease(t *testing.T, u *unstructured.Unstructured, uniqueId string, leaser *leaser.Leaser) {
   164  	shortMinRemaining := 2 * time.Second
   165  	obtainAssertSuccess(t, u, uniqueId, defaultLeaseDuration, defaultLeaseDuration, leaser)
   166  	_, originalExpirationTIme := getOwnerAndExpirationTime(t, u, leaser)
   167  	obtainAssertSuccess(t, u, uniqueId, defaultLeaseDuration, shortMinRemaining, leaser)
   168  	_, expirationTime := getOwnerAndExpirationTime(t, u, leaser)
   169  	if expirationTime != originalExpirationTIme {
   170  		t.Fatalf("expected expiration times to be equal, instead '%v' and '%v'", expirationTime, originalExpirationTIme)
   171  	}
   172  	time.Sleep(shortMinRemaining + 1*time.Second)
   173  	obtainAssertSuccess(t, u, uniqueId, defaultLeaseDuration, defaultLeaseDuration-shortMinRemaining, leaser)
   174  	_, expirationTime = getOwnerAndExpirationTime(t, u, leaser)
   175  	if expirationTime == originalExpirationTIme {
   176  		t.Fatalf("expected expiration times to NOT be equal")
   177  	}
   178  	if originalExpirationTIme.After(expirationTime) {
   179  		t.Fatalf("expected original expiration time '%v' to be after updated expiration time '%v'", originalExpirationTIme, expirationTime)
   180  	}
   181  	releaseAssertSuccess(t, u, uniqueId, leaser)
   182  }
   183  
   184  func getOwnerAndExpirationTime(t *testing.T, u *unstructured.Unstructured, leaser *leaser.Leaser) (string, time.Time) {
   185  	ownerId, expirationTime, err := leaser.GetOwnerAndExpirationTime(context.Background(), u)
   186  	if err != nil {
   187  		t.Fatalf("error getting owner and expiration time: %v", err)
   188  	}
   189  	return ownerId, expirationTime
   190  }
   191  
   192  func obtainAssertSuccess(t *testing.T, u *unstructured.Unstructured, uniqueId string, duration time.Duration, minRemaining time.Duration, leaser *leaser.Leaser) {
   193  	t.Helper()
   194  	err := leaser.Obtain(context.Background(), u, uniqueId, duration, minRemaining)
   195  	if err != nil {
   196  		t.Fatalf("error obtaining lease: %v", err)
   197  	}
   198  }
   199  
   200  func obtainAssertError(t *testing.T, u *unstructured.Unstructured, uniqueId string, duration time.Duration, minRemaining time.Duration, leaser *leaser.Leaser) {
   201  	t.Helper()
   202  	err := leaser.Obtain(context.Background(), u, uniqueId, duration, minRemaining)
   203  	if err == nil {
   204  		t.Fatalf("expected error when obtaining, instead got 'nil'")
   205  	}
   206  }
   207  
   208  func releaseAssertSuccess(t *testing.T, u *unstructured.Unstructured, uniqueId string, leaser *leaser.Leaser) {
   209  	t.Helper()
   210  	err := leaser.Release(context.Background(), u, uniqueId)
   211  	if err != nil {
   212  		t.Fatalf("error obtaining lease: %v", err)
   213  	}
   214  }
   215  
   216  func releaseAssertError(t *testing.T, u *unstructured.Unstructured, uniqueId string, leaser *leaser.Leaser) {
   217  	t.Helper()
   218  	err := leaser.Release(context.Background(), u, uniqueId)
   219  	if err == nil {
   220  		t.Fatalf("expected error when releasing, instead got 'nil'")
   221  	}
   222  }
   223  
   224  func TestMain(m *testing.M) {
   225  	testmain.TestMainForIntegrationTests(m, &mgr)
   226  }
   227  

View as plain text