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 }