github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/backend/remote-state/kubernetes/backend_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package kubernetes 5 6 import ( 7 "context" 8 "fmt" 9 "math/rand" 10 "os" 11 "sync" 12 "testing" 13 "time" 14 15 "github.com/terramate-io/tf/backend" 16 "github.com/terramate-io/tf/states/statemgr" 17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 ) 19 20 const ( 21 secretSuffix = "test-state" 22 ) 23 24 var namespace string 25 26 // verify that we are doing ACC tests or the k8s tests specifically 27 func testACC(t *testing.T) { 28 skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_K8S_TEST") == "" 29 if skip { 30 t.Log("k8s backend tests require setting TF_ACC or TF_K8S_TEST") 31 t.Skip() 32 } 33 34 ns := os.Getenv("KUBE_NAMESPACE") 35 36 if ns != "" { 37 namespace = ns 38 } else { 39 namespace = "default" 40 } 41 42 cleanupK8sResources(t) 43 } 44 45 func TestBackend_impl(t *testing.T) { 46 var _ backend.Backend = new(Backend) 47 } 48 49 func TestBackend(t *testing.T) { 50 testACC(t) 51 defer cleanupK8sResources(t) 52 53 b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 54 "secret_suffix": secretSuffix, 55 })) 56 57 // Test 58 backend.TestBackendStates(t, b1) 59 } 60 61 func TestBackendLocks(t *testing.T) { 62 testACC(t) 63 defer cleanupK8sResources(t) 64 65 // Get the backend. We need two to test locking. 66 b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 67 "secret_suffix": secretSuffix, 68 })) 69 70 b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 71 "secret_suffix": secretSuffix, 72 })) 73 74 // Test 75 backend.TestBackendStateLocks(t, b1, b2) 76 backend.TestBackendStateForceUnlock(t, b1, b2) 77 } 78 79 func TestBackendLocksSoak(t *testing.T) { 80 testACC(t) 81 defer cleanupK8sResources(t) 82 83 clientCount := 100 84 lockAttempts := 100 85 86 lockers := []statemgr.Locker{} 87 for i := 0; i < clientCount; i++ { 88 b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 89 "secret_suffix": secretSuffix, 90 })) 91 92 s, err := b.StateMgr(backend.DefaultStateName) 93 if err != nil { 94 t.Fatalf("Error creating state manager: %v", err) 95 } 96 97 lockers = append(lockers, s.(statemgr.Locker)) 98 } 99 100 wg := sync.WaitGroup{} 101 for i, l := range lockers { 102 wg.Add(1) 103 go func(locker statemgr.Locker, n int) { 104 defer wg.Done() 105 106 li := statemgr.NewLockInfo() 107 li.Operation = "test" 108 li.Who = fmt.Sprintf("client-%v", n) 109 110 for i := 0; i < lockAttempts; i++ { 111 id, err := locker.Lock(li) 112 if err != nil { 113 continue 114 } 115 116 // hold onto the lock for a little bit 117 time.Sleep(time.Duration(rand.Intn(10)) * time.Microsecond) 118 119 err = locker.Unlock(id) 120 if err != nil { 121 t.Errorf("failed to unlock: %v", err) 122 } 123 } 124 }(l, i) 125 } 126 127 wg.Wait() 128 } 129 130 func cleanupK8sResources(t *testing.T) { 131 ctx := context.Background() 132 // Get a backend to use the k8s client 133 b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 134 "secret_suffix": secretSuffix, 135 })) 136 137 b := b1.(*Backend) 138 139 sClient, err := b.KubernetesSecretClient() 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 // Delete secrets 145 opts := metav1.ListOptions{LabelSelector: tfstateKey + "=true"} 146 secrets, err := sClient.List(ctx, opts) 147 if err != nil { 148 t.Fatal(err) 149 } 150 151 delProp := metav1.DeletePropagationBackground 152 delOps := metav1.DeleteOptions{PropagationPolicy: &delProp} 153 var errs []error 154 155 for _, secret := range secrets.Items { 156 labels := secret.GetLabels() 157 key, ok := labels[tfstateSecretSuffixKey] 158 if !ok { 159 continue 160 } 161 162 if key == secretSuffix { 163 err = sClient.Delete(ctx, secret.GetName(), delOps) 164 if err != nil { 165 errs = append(errs, err) 166 } 167 } 168 } 169 170 leaseClient, err := b.KubernetesLeaseClient() 171 if err != nil { 172 t.Fatal(err) 173 } 174 175 // Delete leases 176 leases, err := leaseClient.List(ctx, opts) 177 if err != nil { 178 t.Fatal(err) 179 } 180 181 for _, lease := range leases.Items { 182 labels := lease.GetLabels() 183 key, ok := labels[tfstateSecretSuffixKey] 184 if !ok { 185 continue 186 } 187 188 if key == secretSuffix { 189 err = leaseClient.Delete(ctx, lease.GetName(), delOps) 190 if err != nil { 191 errs = append(errs, err) 192 } 193 } 194 } 195 196 if len(errs) > 0 { 197 t.Fatal(errs) 198 } 199 }