package kubernetes import ( "context" "fmt" "math/rand" "os" "sync" "testing" "time" "github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/states/statemgr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( secretSuffix = "test-state" ) var namespace string // verify that we are doing ACC tests or the k8s tests specifically func testACC(t *testing.T) { skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_K8S_TEST") == "" if skip { t.Log("k8s backend tests require setting TF_ACC or TF_K8S_TEST") t.Skip() } ns := os.Getenv("KUBE_NAMESPACE") if ns != "" { namespace = ns } else { namespace = "default" } cleanupK8sResources(t) } func TestBackend_impl(t *testing.T) { var _ backend.Backend = new(Backend) } func TestBackend(t *testing.T) { testACC(t) defer cleanupK8sResources(t) b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "secret_suffix": secretSuffix, })) // Test backend.TestBackendStates(t, b1) } func TestBackendLocks(t *testing.T) { testACC(t) defer cleanupK8sResources(t) // Get the backend. We need two to test locking. b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "secret_suffix": secretSuffix, })) b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "secret_suffix": secretSuffix, })) // Test backend.TestBackendStateLocks(t, b1, b2) backend.TestBackendStateForceUnlock(t, b1, b2) } func TestBackendLocksSoak(t *testing.T) { testACC(t) defer cleanupK8sResources(t) clientCount := 100 lockAttempts := 100 lockers := []statemgr.Locker{} for i := 0; i < clientCount; i++ { b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "secret_suffix": secretSuffix, })) s, err := b.StateMgr(backend.DefaultStateName) if err != nil { t.Fatalf("Error creating state manager: %v", err) } lockers = append(lockers, s.(statemgr.Locker)) } wg := sync.WaitGroup{} for i, l := range lockers { wg.Add(1) go func(locker statemgr.Locker, n int) { defer wg.Done() li := statemgr.NewLockInfo() li.Operation = "test" li.Who = fmt.Sprintf("client-%v", n) for i := 0; i < lockAttempts; i++ { id, err := locker.Lock(li) if err != nil { continue } // hold onto the lock for a little bit time.Sleep(time.Duration(rand.Intn(10)) * time.Microsecond) err = locker.Unlock(id) if err != nil { t.Errorf("failed to unlock: %v", err) } } }(l, i) } wg.Wait() } func cleanupK8sResources(t *testing.T) { ctx := context.Background() // Get a backend to use the k8s client b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ "secret_suffix": secretSuffix, })) b := b1.(*Backend) sClient, err := b.KubernetesSecretClient() if err != nil { t.Fatal(err) } // Delete secrets opts := metav1.ListOptions{LabelSelector: tfstateKey + "=true"} secrets, err := sClient.List(ctx, opts) if err != nil { t.Fatal(err) } delProp := metav1.DeletePropagationBackground delOps := metav1.DeleteOptions{PropagationPolicy: &delProp} var errs []error for _, secret := range secrets.Items { labels := secret.GetLabels() key, ok := labels[tfstateSecretSuffixKey] if !ok { continue } if key == secretSuffix { err = sClient.Delete(ctx, secret.GetName(), delOps) if err != nil { errs = append(errs, err) } } } leaseClient, err := b.KubernetesLeaseClient() if err != nil { t.Fatal(err) } // Delete leases leases, err := leaseClient.List(ctx, opts) if err != nil { t.Fatal(err) } for _, lease := range leases.Items { labels := lease.GetLabels() key, ok := labels[tfstateSecretSuffixKey] if !ok { continue } if key == secretSuffix { err = leaseClient.Delete(ctx, lease.GetName(), delOps) if err != nil { errs = append(errs, err) } } } if len(errs) > 0 { t.Fatal(errs) } }