kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/backend/remote-state/kubernetes/backend_state.go (about)

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