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 }