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

     1  package kubernetes
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  
     9  	"github.com/hashicorp/terraform/internal/backend"
    10  	"github.com/hashicorp/terraform/internal/states"
    11  	"github.com/hashicorp/terraform/internal/states/remote"
    12  	"github.com/hashicorp/terraform/internal/states/statemgr"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  )
    15  
    16  // Workspaces returns a list of names for the workspaces found in k8s. The default
    17  // workspace is always returned as the first element in the slice.
    18  func (b *Backend) Workspaces() ([]string, error) {
    19  	secretClient, err := b.KubernetesSecretClient()
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  
    24  	secrets, err := secretClient.List(
    25  		context.Background(),
    26  		metav1.ListOptions{
    27  			LabelSelector: tfstateKey + "=true",
    28  		},
    29  	)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	// Use a map so there aren't duplicate workspaces
    35  	m := make(map[string]struct{})
    36  	for _, secret := range secrets.Items {
    37  		sl := secret.GetLabels()
    38  		ws, ok := sl[tfstateWorkspaceKey]
    39  		if !ok {
    40  			continue
    41  		}
    42  
    43  		key, ok := sl[tfstateSecretSuffixKey]
    44  		if !ok {
    45  			continue
    46  		}
    47  
    48  		// Make sure it isn't default and the key matches
    49  		if ws != backend.DefaultStateName && key == b.nameSuffix {
    50  			m[ws] = struct{}{}
    51  		}
    52  	}
    53  
    54  	states := []string{backend.DefaultStateName}
    55  	for k := range m {
    56  		states = append(states, k)
    57  	}
    58  
    59  	sort.Strings(states[1:])
    60  	return states, nil
    61  }
    62  
    63  func (b *Backend) DeleteWorkspace(name string, _ bool) error {
    64  	if name == backend.DefaultStateName || name == "" {
    65  		return fmt.Errorf("can't delete default state")
    66  	}
    67  
    68  	client, err := b.remoteClient(name)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	return client.Delete()
    74  }
    75  
    76  func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
    77  	c, err := b.remoteClient(name)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	stateMgr := &remote.State{Client: c}
    83  
    84  	// Grab the value
    85  	if err := stateMgr.RefreshState(); err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	// If we have no state, we have to create an empty state
    90  	if v := stateMgr.State(); v == nil {
    91  
    92  		lockInfo := statemgr.NewLockInfo()
    93  		lockInfo.Operation = "init"
    94  		lockID, err := stateMgr.Lock(lockInfo)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  
    99  		secretName, err := c.createSecretName()
   100  		if err != nil {
   101  			return nil, err
   102  		}
   103  
   104  		// Local helper function so we can call it multiple places
   105  		unlock := func(baseErr error) error {
   106  			if err := stateMgr.Unlock(lockID); err != nil {
   107  				const unlockErrMsg = `%v
   108  				Additionally, unlocking the state in Kubernetes failed:
   109  
   110  				Error message: %q
   111  				Lock ID (gen): %v
   112  				Secret Name: %v
   113  
   114  				You may have to force-unlock this state in order to use it again.
   115  				The Kubernetes backend acquires a lock during initialization to ensure
   116  				the initial state file is created.`
   117  				return fmt.Errorf(unlockErrMsg, baseErr, err.Error(), lockID, secretName)
   118  			}
   119  
   120  			return baseErr
   121  		}
   122  
   123  		if err := stateMgr.WriteState(states.NewState()); err != nil {
   124  			return nil, unlock(err)
   125  		}
   126  		if err := stateMgr.PersistState(nil); err != nil {
   127  			return nil, unlock(err)
   128  		}
   129  
   130  		// Unlock, the state should now be initialized
   131  		if err := unlock(nil); err != nil {
   132  			return nil, err
   133  		}
   134  
   135  	}
   136  
   137  	return stateMgr, nil
   138  }
   139  
   140  // get a remote client configured for this state
   141  func (b *Backend) remoteClient(name string) (*RemoteClient, error) {
   142  	if name == "" {
   143  		return nil, errors.New("missing state name")
   144  	}
   145  
   146  	secretClient, err := b.KubernetesSecretClient()
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	leaseClient, err := b.KubernetesLeaseClient()
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	client := &RemoteClient{
   157  		kubernetesSecretClient: secretClient,
   158  		kubernetesLeaseClient:  leaseClient,
   159  		namespace:              b.namespace,
   160  		labels:                 b.labels,
   161  		nameSuffix:             b.nameSuffix,
   162  		workspace:              name,
   163  	}
   164  
   165  	return client, nil
   166  }
   167  
   168  func (b *Backend) client() *RemoteClient {
   169  	return &RemoteClient{}
   170  }