github.com/hugorut/terraform@v1.1.3/src/backend/remote-state/manta/backend_state.go (about) 1 package manta 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "path" 8 "sort" 9 "strings" 10 11 tritonErrors "github.com/joyent/triton-go/errors" 12 "github.com/joyent/triton-go/storage" 13 14 "github.com/hugorut/terraform/src/backend" 15 "github.com/hugorut/terraform/src/states" 16 "github.com/hugorut/terraform/src/states/remote" 17 "github.com/hugorut/terraform/src/states/statemgr" 18 ) 19 20 func (b *Backend) Workspaces() ([]string, error) { 21 result := []string{backend.DefaultStateName} 22 23 objs, err := b.storageClient.Dir().List(context.Background(), &storage.ListDirectoryInput{ 24 DirectoryName: path.Join(mantaDefaultRootStore, b.path), 25 }) 26 if err != nil { 27 if tritonErrors.IsResourceNotFound(err) { 28 return result, nil 29 } 30 return nil, err 31 } 32 33 for _, obj := range objs.Entries { 34 if obj.Type == "directory" && obj.Name != "" { 35 result = append(result, obj.Name) 36 } 37 } 38 39 sort.Strings(result[1:]) 40 return result, nil 41 } 42 43 func (b *Backend) DeleteWorkspace(name string) error { 44 if name == backend.DefaultStateName || name == "" { 45 return fmt.Errorf("can't delete default state") 46 } 47 48 //firstly we need to delete the state file 49 err := b.storageClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ 50 ObjectPath: path.Join(mantaDefaultRootStore, b.statePath(name), b.objectName), 51 }) 52 if err != nil { 53 return err 54 } 55 56 //then we need to delete the state folder 57 err = b.storageClient.Objects().Delete(context.Background(), &storage.DeleteObjectInput{ 58 ObjectPath: path.Join(mantaDefaultRootStore, b.statePath(name)), 59 }) 60 if err != nil { 61 return err 62 } 63 64 return nil 65 } 66 67 func (b *Backend) StateMgr(name string) (statemgr.Full, error) { 68 if name == "" { 69 return nil, errors.New("missing state name") 70 } 71 72 client := &RemoteClient{ 73 storageClient: b.storageClient, 74 directoryName: b.statePath(name), 75 keyName: b.objectName, 76 } 77 78 stateMgr := &remote.State{Client: client} 79 80 //if this isn't the default state name, we need to create the object so 81 //it's listed by States. 82 if name != backend.DefaultStateName { 83 // take a lock on this state while we write it 84 lockInfo := statemgr.NewLockInfo() 85 lockInfo.Operation = "init" 86 lockId, err := client.Lock(lockInfo) 87 if err != nil { 88 return nil, fmt.Errorf("failed to lock manta state: %s", err) 89 } 90 91 // Local helper function so we can call it multiple places 92 lockUnlock := func(parent error) error { 93 if err := stateMgr.Unlock(lockId); err != nil { 94 return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) 95 } 96 return parent 97 } 98 99 // Grab the value 100 if err := stateMgr.RefreshState(); err != nil { 101 err = lockUnlock(err) 102 return nil, err 103 } 104 105 // If we have no state, we have to create an empty state 106 if v := stateMgr.State(); v == nil { 107 if err := stateMgr.WriteState(states.NewState()); err != nil { 108 err = lockUnlock(err) 109 return nil, err 110 } 111 if err := stateMgr.PersistState(); err != nil { 112 err = lockUnlock(err) 113 return nil, err 114 } 115 } 116 117 // Unlock, the state should now be initialized 118 if err := lockUnlock(nil); err != nil { 119 return nil, err 120 } 121 122 } 123 124 return stateMgr, nil 125 } 126 127 func (b *Backend) client() *RemoteClient { 128 return &RemoteClient{} 129 } 130 131 func (b *Backend) statePath(name string) string { 132 if name == backend.DefaultStateName { 133 return b.path 134 } 135 136 return path.Join(b.path, name) 137 } 138 139 const errStateUnlock = ` 140 Error unlocking Manta state. Lock ID: %s 141 142 Error: %s 143 144 You may have to force-unlock this state in order to use it again. 145 `