2020-04-13 22:17:17 +02:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
2020-06-05 19:32:59 +02:00
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
2020-04-13 22:17:17 +02:00
|
|
|
"os"
|
2020-06-05 19:32:59 +02:00
|
|
|
"sync"
|
2020-04-13 22:17:17 +02:00
|
|
|
"testing"
|
2020-06-05 19:32:59 +02:00
|
|
|
"time"
|
2020-04-13 22:17:17 +02:00
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/backend"
|
2020-06-05 19:32:59 +02:00
|
|
|
"github.com/hashicorp/terraform/states/statemgr"
|
2020-04-13 22:17:17 +02:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2020-06-05 19:32:59 +02:00
|
|
|
cleanupK8sResources(t)
|
2020-04-13 22:17:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestBackend_impl(t *testing.T) {
|
|
|
|
var _ backend.Backend = new(Backend)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBackend(t *testing.T) {
|
|
|
|
testACC(t)
|
2020-06-05 19:32:59 +02:00
|
|
|
defer cleanupK8sResources(t)
|
2020-04-13 22:17:17 +02:00
|
|
|
|
|
|
|
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)
|
2020-06-05 19:32:59 +02:00
|
|
|
defer cleanupK8sResources(t)
|
2020-04-13 22:17:17 +02:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2020-06-05 19:32:59 +02:00
|
|
|
func TestBackendLocksSoak(t *testing.T) {
|
|
|
|
testACC(t)
|
|
|
|
defer cleanupK8sResources(t)
|
|
|
|
|
2020-06-06 00:11:20 +02:00
|
|
|
clientCount := 100
|
|
|
|
lockAttempts := 100
|
2020-06-05 19:32:59 +02:00
|
|
|
|
|
|
|
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)
|
2020-06-06 00:11:20 +02:00
|
|
|
go func(locker statemgr.Locker, n int) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2020-08-11 17:43:01 +02:00
|
|
|
li := statemgr.NewLockInfo()
|
2020-06-05 19:32:59 +02:00
|
|
|
li.Operation = "test"
|
2020-06-06 00:11:20 +02:00
|
|
|
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)
|
|
|
|
}
|
2020-06-05 19:32:59 +02:00
|
|
|
}
|
|
|
|
}(l, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
func cleanupK8sResources(t *testing.T) {
|
2020-04-13 22:17:17 +02:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2020-06-05 19:32:59 +02:00
|
|
|
// Delete secrets
|
2020-04-13 22:17:17 +02:00
|
|
|
opts := metav1.ListOptions{LabelSelector: tfstateKey + "=true"}
|
|
|
|
secrets, err := sClient.List(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(secret.GetName(), delOps)
|
|
|
|
if err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-05 19:32:59 +02:00
|
|
|
leaseClient, err := b.KubernetesLeaseClient()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete leases
|
|
|
|
leases, err := leaseClient.List(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(lease.GetName(), delOps)
|
|
|
|
if err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 22:17:17 +02:00
|
|
|
if len(errs) > 0 {
|
|
|
|
t.Fatal(errs)
|
|
|
|
}
|
|
|
|
}
|