github.com/ctrox/terraform@v0.11.12-beta1/backend/remote-state/consul/client_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/hashicorp/terraform/backend"
    12  	"github.com/hashicorp/terraform/state"
    13  	"github.com/hashicorp/terraform/state/remote"
    14  )
    15  
    16  func TestRemoteClient_impl(t *testing.T) {
    17  	var _ remote.Client = new(RemoteClient)
    18  	var _ remote.ClientLocker = new(RemoteClient)
    19  }
    20  
    21  func TestRemoteClient(t *testing.T) {
    22  	// Get the backend
    23  	b := backend.TestBackendConfig(t, New(), map[string]interface{}{
    24  		"address": srv.HTTPAddr,
    25  		"path":    fmt.Sprintf("tf-unit/%s", time.Now().String()),
    26  	})
    27  
    28  	// Grab the client
    29  	state, err := b.State(backend.DefaultStateName)
    30  	if err != nil {
    31  		t.Fatalf("err: %s", err)
    32  	}
    33  
    34  	// Test
    35  	remote.TestClient(t, state.(*remote.State).Client)
    36  }
    37  
    38  // test the gzip functionality of the client
    39  func TestRemoteClient_gzipUpgrade(t *testing.T) {
    40  	statePath := fmt.Sprintf("tf-unit/%s", time.Now().String())
    41  
    42  	// Get the backend
    43  	b := backend.TestBackendConfig(t, New(), map[string]interface{}{
    44  		"address": srv.HTTPAddr,
    45  		"path":    statePath,
    46  	})
    47  
    48  	// Grab the client
    49  	state, err := b.State(backend.DefaultStateName)
    50  	if err != nil {
    51  		t.Fatalf("err: %s", err)
    52  	}
    53  
    54  	// Test
    55  	remote.TestClient(t, state.(*remote.State).Client)
    56  
    57  	// create a new backend with gzip
    58  	b = backend.TestBackendConfig(t, New(), map[string]interface{}{
    59  		"address": srv.HTTPAddr,
    60  		"path":    statePath,
    61  		"gzip":    true,
    62  	})
    63  
    64  	// Grab the client
    65  	state, err = b.State(backend.DefaultStateName)
    66  	if err != nil {
    67  		t.Fatalf("err: %s", err)
    68  	}
    69  
    70  	// Test
    71  	remote.TestClient(t, state.(*remote.State).Client)
    72  }
    73  
    74  func TestConsul_stateLock(t *testing.T) {
    75  	path := fmt.Sprintf("tf-unit/%s", time.Now().String())
    76  
    77  	// create 2 instances to get 2 remote.Clients
    78  	sA, err := backend.TestBackendConfig(t, New(), map[string]interface{}{
    79  		"address": srv.HTTPAddr,
    80  		"path":    path,
    81  	}).State(backend.DefaultStateName)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	sB, err := backend.TestBackendConfig(t, New(), map[string]interface{}{
    87  		"address": srv.HTTPAddr,
    88  		"path":    path,
    89  	}).State(backend.DefaultStateName)
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client)
    95  }
    96  
    97  func TestConsul_destroyLock(t *testing.T) {
    98  	// Get the backend
    99  	b := backend.TestBackendConfig(t, New(), map[string]interface{}{
   100  		"address": srv.HTTPAddr,
   101  		"path":    fmt.Sprintf("tf-unit/%s", time.Now().String()),
   102  	})
   103  
   104  	// Grab the client
   105  	s, err := b.State(backend.DefaultStateName)
   106  	if err != nil {
   107  		t.Fatalf("err: %s", err)
   108  	}
   109  
   110  	c := s.(*remote.State).Client.(*RemoteClient)
   111  
   112  	info := state.NewLockInfo()
   113  	id, err := c.Lock(info)
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	lockPath := c.Path + lockSuffix
   119  
   120  	if err := c.Unlock(id); err != nil {
   121  		t.Fatal(err)
   122  	}
   123  
   124  	// get the lock val
   125  	pair, _, err := c.Client.KV().Get(lockPath, nil)
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	if pair != nil {
   130  		t.Fatalf("lock key not cleaned up at: %s", pair.Key)
   131  	}
   132  }
   133  
   134  func TestConsul_lostLock(t *testing.T) {
   135  	path := fmt.Sprintf("tf-unit/%s", time.Now().String())
   136  
   137  	// create 2 instances to get 2 remote.Clients
   138  	sA, err := backend.TestBackendConfig(t, New(), map[string]interface{}{
   139  		"address": srv.HTTPAddr,
   140  		"path":    path,
   141  	}).State(backend.DefaultStateName)
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	sB, err := backend.TestBackendConfig(t, New(), map[string]interface{}{
   147  		"address": srv.HTTPAddr,
   148  		"path":    path + "-not-used",
   149  	}).State(backend.DefaultStateName)
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  
   154  	info := state.NewLockInfo()
   155  	info.Operation = "test-lost-lock"
   156  	id, err := sA.Lock(info)
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  
   161  	reLocked := make(chan struct{})
   162  	testLockHook = func() {
   163  		close(reLocked)
   164  		testLockHook = nil
   165  	}
   166  
   167  	// now we use the second client to break the lock
   168  	kv := sB.(*remote.State).Client.(*RemoteClient).Client.KV()
   169  	_, err = kv.Delete(path+lockSuffix, nil)
   170  	if err != nil {
   171  		t.Fatal(err)
   172  	}
   173  
   174  	<-reLocked
   175  
   176  	if err := sA.Unlock(id); err != nil {
   177  		t.Fatal(err)
   178  	}
   179  }
   180  
   181  func TestConsul_lostLockConnection(t *testing.T) {
   182  	// create an "unreliable" network by closing all the consul client's
   183  	// network connections
   184  	conns := &unreliableConns{}
   185  	origDialFn := dialContext
   186  	defer func() {
   187  		dialContext = origDialFn
   188  	}()
   189  	dialContext = conns.DialContext
   190  
   191  	path := fmt.Sprintf("tf-unit/%s", time.Now().String())
   192  
   193  	b := backend.TestBackendConfig(t, New(), map[string]interface{}{
   194  		"address": srv.HTTPAddr,
   195  		"path":    path,
   196  	})
   197  
   198  	s, err := b.State(backend.DefaultStateName)
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	info := state.NewLockInfo()
   204  	info.Operation = "test-lost-lock-connection"
   205  	id, err := s.Lock(info)
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  
   210  	// kill the connection a few times
   211  	for i := 0; i < 3; i++ {
   212  		dialed := conns.dialedDone()
   213  		// kill any open connections
   214  		conns.Kill()
   215  		// wait for a new connection to be dialed, and kill it again
   216  		<-dialed
   217  	}
   218  
   219  	if err := s.Unlock(id); err != nil {
   220  		t.Fatal("unlock error:", err)
   221  	}
   222  }
   223  
   224  type unreliableConns struct {
   225  	sync.Mutex
   226  	conns        []net.Conn
   227  	dialCallback func()
   228  }
   229  
   230  func (u *unreliableConns) DialContext(ctx context.Context, netw, addr string) (net.Conn, error) {
   231  	u.Lock()
   232  	defer u.Unlock()
   233  
   234  	dialer := &net.Dialer{}
   235  	conn, err := dialer.DialContext(ctx, netw, addr)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	u.conns = append(u.conns, conn)
   241  
   242  	if u.dialCallback != nil {
   243  		u.dialCallback()
   244  	}
   245  
   246  	return conn, nil
   247  }
   248  
   249  func (u *unreliableConns) dialedDone() chan struct{} {
   250  	u.Lock()
   251  	defer u.Unlock()
   252  	dialed := make(chan struct{})
   253  	u.dialCallback = func() {
   254  		defer close(dialed)
   255  		u.dialCallback = nil
   256  	}
   257  
   258  	return dialed
   259  }
   260  
   261  // Kill these with a deadline, just to make sure we don't end up with any EOFs
   262  // that get ignored.
   263  func (u *unreliableConns) Kill() {
   264  	u.Lock()
   265  	defer u.Unlock()
   266  
   267  	for _, conn := range u.conns {
   268  		conn.(*net.TCPConn).SetDeadline(time.Now())
   269  	}
   270  	u.conns = nil
   271  }