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