github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/backend/remote-state/consul/backend_state.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/terraform/internal/backend"
     8  	"github.com/hashicorp/terraform/internal/states"
     9  	"github.com/hashicorp/terraform/internal/states/remote"
    10  	"github.com/hashicorp/terraform/internal/states/statemgr"
    11  )
    12  
    13  const (
    14  	keyEnvPrefix = "-env:"
    15  )
    16  
    17  func (b *Backend) Workspaces() ([]string, error) {
    18  	// List our raw path
    19  	prefix := b.configData.Get("path").(string) + keyEnvPrefix
    20  	keys, _, err := b.client.KV().Keys(prefix, "/", nil)
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  
    25  	// Find the envs, we use a map since we can get duplicates with
    26  	// path suffixes.
    27  	envs := map[string]struct{}{}
    28  	for _, key := range keys {
    29  		// Consul should ensure this but it doesn't hurt to check again
    30  		if strings.HasPrefix(key, prefix) {
    31  			key = strings.TrimPrefix(key, prefix)
    32  
    33  			// Ignore anything with a "/" in it since we store the state
    34  			// directly in a key not a directory.
    35  			if idx := strings.IndexRune(key, '/'); idx >= 0 {
    36  				continue
    37  			}
    38  
    39  			envs[key] = struct{}{}
    40  		}
    41  	}
    42  
    43  	result := make([]string, 1, len(envs)+1)
    44  	result[0] = backend.DefaultStateName
    45  	for k, _ := range envs {
    46  		result = append(result, k)
    47  	}
    48  
    49  	return result, nil
    50  }
    51  
    52  func (b *Backend) DeleteWorkspace(name string, _ bool) error {
    53  	if name == backend.DefaultStateName || name == "" {
    54  		return fmt.Errorf("can't delete default state")
    55  	}
    56  
    57  	// Determine the path of the data
    58  	path := b.path(name)
    59  
    60  	// Delete it. We just delete it without any locking since
    61  	// the DeleteState API is documented as such.
    62  	_, err := b.client.KV().Delete(path, nil)
    63  	return err
    64  }
    65  
    66  func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
    67  	// Determine the path of the data
    68  	path := b.path(name)
    69  
    70  	// Determine whether to gzip or not
    71  	gzip := b.configData.Get("gzip").(bool)
    72  
    73  	// Build the state client
    74  	var stateMgr = &remote.State{
    75  		Client: &RemoteClient{
    76  			Client:    b.client,
    77  			Path:      path,
    78  			GZip:      gzip,
    79  			lockState: b.lock,
    80  		},
    81  	}
    82  
    83  	if !b.lock {
    84  		stateMgr.DisableLocks()
    85  	}
    86  
    87  	// the default state always exists
    88  	if name == backend.DefaultStateName {
    89  		return stateMgr, nil
    90  	}
    91  
    92  	// Grab a lock, we use this to write an empty state if one doesn't
    93  	// exist already. We have to write an empty state as a sentinel value
    94  	// so States() knows it exists.
    95  	lockInfo := statemgr.NewLockInfo()
    96  	lockInfo.Operation = "init"
    97  	lockId, err := stateMgr.Lock(lockInfo)
    98  	if err != nil {
    99  		return nil, fmt.Errorf("failed to lock state in Consul: %s", err)
   100  	}
   101  
   102  	// Local helper function so we can call it multiple places
   103  	lockUnlock := func(parent error) error {
   104  		if err := stateMgr.Unlock(lockId); err != nil {
   105  			return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
   106  		}
   107  
   108  		return parent
   109  	}
   110  
   111  	// Grab the value
   112  	if err := stateMgr.RefreshState(); err != nil {
   113  		err = lockUnlock(err)
   114  		return nil, err
   115  	}
   116  
   117  	// If we have no state, we have to create an empty state
   118  	if v := stateMgr.State(); v == nil {
   119  		if err := stateMgr.WriteState(states.NewState()); err != nil {
   120  			err = lockUnlock(err)
   121  			return nil, err
   122  		}
   123  		if err := stateMgr.PersistState(nil); err != nil {
   124  			err = lockUnlock(err)
   125  			return nil, err
   126  		}
   127  	}
   128  
   129  	// Unlock, the state should now be initialized
   130  	if err := lockUnlock(nil); err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	return stateMgr, nil
   135  }
   136  
   137  func (b *Backend) path(name string) string {
   138  	path := b.configData.Get("path").(string)
   139  	if name != backend.DefaultStateName {
   140  		path += fmt.Sprintf("%s%s", keyEnvPrefix, name)
   141  	}
   142  
   143  	return path
   144  }
   145  
   146  const errStateUnlock = `
   147  Error unlocking Consul state. Lock ID: %s
   148  
   149  Error: %s
   150  
   151  You may have to force-unlock this state in order to use it again.
   152  The Consul backend acquires a lock during initialization to ensure
   153  the minimum required key/values are prepared.
   154  `