Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sanitize lock path for the Consul backend when it ends with a / #25842

Merged
merged 1 commit into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/remote-state/consul/backend_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package consul

import (
"flag"
"fmt"
"io/ioutil"
"os"
Expand Down Expand Up @@ -39,6 +40,10 @@ func newConsulTestServer() (*testutil.TestServer, error) {
srv, err := testutil.NewTestServerConfig(func(c *testutil.TestServerConfig) {
c.LogLevel = "warn"

if !flag.Parsed() {
flag.Parse()
}

if !testing.Verbose() {
c.Stdout = ioutil.Discard
c.Stderr = ioutil.Discard
Expand Down
15 changes: 11 additions & 4 deletions backend/remote-state/consul/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"log"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -161,21 +162,27 @@ func (c *RemoteClient) Delete() error {
return err
}

func (c *RemoteClient) lockPath() string {
// we sanitize the path for the lock as Consul does not like having
// two consecutive slashes for the lock path
return strings.TrimRight(c.Path, "/")
}

func (c *RemoteClient) putLockInfo(info *statemgr.LockInfo) error {
info.Path = c.Path
info.Created = time.Now().UTC()

kv := c.Client.KV()
_, err := kv.Put(&consulapi.KVPair{
Key: c.Path + lockInfoSuffix,
Key: c.lockPath() + lockInfoSuffix,
Value: info.Marshal(),
}, nil)

return err
}

func (c *RemoteClient) getLockInfo() (*statemgr.LockInfo, error) {
path := c.Path + lockInfoSuffix
path := c.lockPath() + lockInfoSuffix
pair, _, err := c.Client.KV().Get(path, nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -234,7 +241,7 @@ func (c *RemoteClient) lock() (string, error) {
c.info.Info = "consul session: " + lockSession

opts := &consulapi.LockOptions{
Key: c.Path + lockSuffix,
Key: c.lockPath() + lockSuffix,
Session: lockSession,

// only wait briefly, so terraform has the choice to fail fast or
Expand Down Expand Up @@ -419,7 +426,7 @@ func (c *RemoteClient) unlock(id string) error {

var errs error

if _, err := kv.Delete(c.Path+lockInfoSuffix, nil); err != nil {
if _, err := kv.Delete(c.lockPath()+lockInfoSuffix, nil); err != nil {
errs = multierror.Append(errs, err)
}

Expand Down
145 changes: 85 additions & 60 deletions backend/remote-state/consul/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,29 @@ func TestRemoteClient_impl(t *testing.T) {
}

func TestRemoteClient(t *testing.T) {
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
}))

// Grab the client
state, err := b.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatalf("err: %s", err)
testCases := []string{
fmt.Sprintf("tf-unit/%s", time.Now().String()),
fmt.Sprintf("tf-unit/%s/", time.Now().String()),
}

// Test
remote.TestClient(t, state.(*remote.State).Client)
for _, path := range testCases {
t.Run(path, func(*testing.T) {
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))

// Grab the client
state, err := b.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatalf("err: %s", err)
}

// Test
remote.TestClient(t, state.(*remote.State).Client)
})
}
}

// test the gzip functionality of the client
Expand Down Expand Up @@ -72,62 +81,78 @@ func TestRemoteClient_gzipUpgrade(t *testing.T) {
}

func TestConsul_stateLock(t *testing.T) {
path := fmt.Sprintf("tf-unit/%s", time.Now().String())

// create 2 instances to get 2 remote.Clients
sA, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
})).StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
testCases := []string{
fmt.Sprintf("tf-unit/%s", time.Now().String()),
fmt.Sprintf("tf-unit/%s/", time.Now().String()),
}

sB, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
})).StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
for _, path := range testCases {
t.Run(path, func(*testing.T) {
// create 2 instances to get 2 remote.Clients
sA, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
})).StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
}

sB, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
})).StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
}

remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client)
})
}

remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client)
}

func TestConsul_destroyLock(t *testing.T) {
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
}))

// Grab the client
s, err := b.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatalf("err: %s", err)
testCases := []string{
fmt.Sprintf("tf-unit/%s", time.Now().String()),
fmt.Sprintf("tf-unit/%s/", time.Now().String()),
}

c := s.(*remote.State).Client.(*RemoteClient)

info := statemgr.NewLockInfo()
id, err := c.Lock(info)
if err != nil {
t.Fatal(err)
}

lockPath := c.Path + lockSuffix

if err := c.Unlock(id); err != nil {
t.Fatal(err)
}

// get the lock val
pair, _, err := c.Client.KV().Get(lockPath, nil)
if err != nil {
t.Fatal(err)
}
if pair != nil {
t.Fatalf("lock key not cleaned up at: %s", pair.Key)
for _, path := range testCases {
t.Run(path, func(*testing.T) {
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))

// Grab the client
s, err := b.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatalf("err: %s", err)
}

c := s.(*remote.State).Client.(*RemoteClient)

info := statemgr.NewLockInfo()
id, err := c.Lock(info)
if err != nil {
t.Fatal(err)
}

lockPath := c.Path + lockSuffix

if err := c.Unlock(id); err != nil {
t.Fatal(err)
}

// get the lock val
pair, _, err := c.Client.KV().Get(lockPath, nil)
if err != nil {
t.Fatal(err)
}
if pair != nil {
t.Fatalf("lock key not cleaned up at: %s", pair.Key)
}
})
}
}

Expand Down