github.com/sathish1597/hashicorp-terraform@v0.11.12-beta1/backend/remote-state/azure/backend_state.go (about) 1 package azure 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/Azure/azure-sdk-for-go/storage" 9 "github.com/hashicorp/terraform/backend" 10 "github.com/hashicorp/terraform/state" 11 "github.com/hashicorp/terraform/state/remote" 12 "github.com/hashicorp/terraform/terraform" 13 ) 14 15 const ( 16 // This will be used as directory name, the odd looking colon is simply to 17 // reduce the chance of name conflicts with existing objects. 18 keyEnvPrefix = "env:" 19 ) 20 21 func (b *Backend) States() ([]string, error) { 22 prefix := b.keyName + keyEnvPrefix 23 params := storage.ListBlobsParameters{ 24 Prefix: prefix, 25 } 26 27 container := b.blobClient.GetContainerReference(b.containerName) 28 resp, err := container.ListBlobs(params) 29 if err != nil { 30 return nil, err 31 } 32 33 envs := map[string]struct{}{} 34 for _, obj := range resp.Blobs { 35 key := obj.Name 36 if strings.HasPrefix(key, prefix) { 37 name := strings.TrimPrefix(key, prefix) 38 // we store the state in a key, not a directory 39 if strings.Contains(name, "/") { 40 continue 41 } 42 43 envs[name] = struct{}{} 44 } 45 } 46 47 result := []string{backend.DefaultStateName} 48 for name := range envs { 49 result = append(result, name) 50 } 51 sort.Strings(result[1:]) 52 return result, nil 53 } 54 55 func (b *Backend) DeleteState(name string) error { 56 if name == backend.DefaultStateName || name == "" { 57 return fmt.Errorf("can't delete default state") 58 } 59 60 containerReference := b.blobClient.GetContainerReference(b.containerName) 61 blobReference := containerReference.GetBlobReference(b.path(name)) 62 options := &storage.DeleteBlobOptions{} 63 64 return blobReference.Delete(options) 65 } 66 67 func (b *Backend) State(name string) (state.State, error) { 68 client := &RemoteClient{ 69 blobClient: b.blobClient, 70 containerName: b.containerName, 71 keyName: b.path(name), 72 } 73 74 stateMgr := &remote.State{Client: client} 75 76 //if this isn't the default state name, we need to create the object so 77 //it's listed by States. 78 if name != backend.DefaultStateName { 79 // take a lock on this state while we write it 80 lockInfo := state.NewLockInfo() 81 lockInfo.Operation = "init" 82 lockId, err := client.Lock(lockInfo) 83 if err != nil { 84 return nil, fmt.Errorf("failed to lock azure state: %s", err) 85 } 86 87 // Local helper function so we can call it multiple places 88 lockUnlock := func(parent error) error { 89 if err := stateMgr.Unlock(lockId); err != nil { 90 return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) 91 } 92 return parent 93 } 94 95 // Grab the value 96 if err := stateMgr.RefreshState(); err != nil { 97 err = lockUnlock(err) 98 return nil, err 99 } 100 101 // If we have no state, we have to create an empty state 102 if v := stateMgr.State(); v == nil { 103 if err := stateMgr.WriteState(terraform.NewState()); err != nil { 104 err = lockUnlock(err) 105 return nil, err 106 } 107 if err := stateMgr.PersistState(); err != nil { 108 err = lockUnlock(err) 109 return nil, err 110 } 111 } 112 113 // Unlock, the state should now be initialized 114 if err := lockUnlock(nil); err != nil { 115 return nil, err 116 } 117 118 } 119 120 return stateMgr, nil 121 } 122 123 func (b *Backend) client() *RemoteClient { 124 return &RemoteClient{} 125 } 126 127 func (b *Backend) path(name string) string { 128 if name == backend.DefaultStateName { 129 return b.keyName 130 } 131 132 return b.keyName + keyEnvPrefix + name 133 } 134 135 const errStateUnlock = ` 136 Error unlocking Azure state. Lock ID: %s 137 138 Error: %s 139 140 You may have to force-unlock this state in order to use it again. 141 `