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