-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbackend.go
167 lines (145 loc) · 4.41 KB
/
backend.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
// ConflictError if there is a locking conflict
type ConflictError struct {
StatusCode int
}
func (c *ConflictError) Error() string {
return fmt.Sprintf("status %d", c.StatusCode)
}
// FileNotExistsError used if the file not exists
type FileNotExistsError struct {
Info string
}
func (n *FileNotExistsError) Error() string {
return n.Info
}
// LockInfo copied from https://github.com/hashicorp/terraform/blob/main/internal/states/statemgr/locker.go#L115
type LockInfo struct {
ID string
Operation string
Info string
Who string
Version string
Created time.Time
Path string
}
// Backend struct to serve as backend for terraform http backend storage
type Backend struct {
dir string
}
func (b *Backend) getTfstateFilename(tfID string) string {
if strings.HasSuffix(tfID, ".tfstate") {
return filepath.Join(b.dir, tfID)
}
return filepath.Join(b.dir, tfID+".tfstate")
}
func (b *Backend) get(tfID string) ([]byte, error) {
var tfstateFilename = b.getTfstateFilename(tfID)
var tfstate []byte
var err error
if _, err := os.Stat(tfstateFilename); os.IsNotExist(err) {
logger.Infof("File %s not found", tfstateFilename)
return nil, err
}
if tfstate, err = ioutil.ReadFile(tfstateFilename); err != nil {
logger.Warnf("Can't read file %s. With follow error %v", tfstateFilename, err)
return nil, err
}
return tfstate, nil
}
func (b *Backend) update(tfID string, tfstate []byte) error {
var tfstateFilename = b.getTfstateFilename(tfID)
if err := ioutil.WriteFile(tfstateFilename, tfstate, 0644); err != nil {
logger.Warnf("Can't write file %s. Got follow error %v", tfstateFilename, err)
return err
}
return nil
}
func (b *Backend) purge(tfID string) error {
var log = GetLogger()
var tfstateFilename = b.getTfstateFilename(tfID)
if _, err := os.Stat(tfstateFilename); os.IsNotExist(err) {
log.Infof("File %s not found", tfstateFilename)
return nil
}
if err := os.Remove(tfstateFilename); err != nil {
log.Warnf("Can't delete file %s. Got follow error %v", tfstateFilename, err)
return err
}
return nil
}
func (b *Backend) lock(tfID string, lock []byte) ([]byte, error) {
var lockFilename string = b.dir + tfID + ".lock"
var lockFile []byte
var lockInfo, currentLockInfo LockInfo
var err error
if err := json.Unmarshal(lock, &lockInfo); err != nil {
logger.Errorf("unexpected decoding json error %v", err)
return nil, err
}
if _, err := os.Stat(lockFilename); os.IsNotExist(err) {
if err = ioutil.WriteFile(lockFilename, lock, 0644); err != nil {
logger.Errorf("Can't write lock file %s. Got follow error %v", lockFilename, err)
return nil, err
}
return lock, nil
}
if lockFile, err = ioutil.ReadFile(lockFilename); err != nil {
logger.Errorf("Can't read file %s. With follow error %v", lockFilename, err)
return nil, err
}
if err := json.Unmarshal(lockFile, ¤tLockInfo); err != nil {
logger.Errorf("unexpected decoding json error %v", err)
return nil, err
}
if currentLockInfo.ID != lockInfo.ID {
logger.Infof("state is locked with diffrend id %s, but follow id requestd lock %s", currentLockInfo.ID, lockInfo.ID)
return nil, &ConflictError{
StatusCode: http.StatusConflict,
}
}
return lockFile, nil
}
func (b *Backend) unlock(tfID string, lock []byte) error {
var lockFilename string = b.dir + tfID + ".lock"
var lockFile []byte
var err error
var lockInfo, currentLockInfo LockInfo
if err := json.Unmarshal(lock, &lockInfo); err != nil {
logger.Errorf("unexpected decoding json error %v", err)
return err
}
if _, err := os.Stat(lockFilename); os.IsNotExist(err) {
logger.Infof("lock file %s is deleted so notting to do.", lockFilename)
return nil
}
if lockFile, err = ioutil.ReadFile(lockFilename); err != nil {
logger.Errorf("Can't read file %s. With follow error %v", lockFilename, err)
return err
}
if err := json.Unmarshal(lockFile, ¤tLockInfo); err != nil {
logger.Errorf("unexpected decoding json error %v", err)
return err
}
if currentLockInfo.ID != lockInfo.ID {
logger.Infof("state is locked with diffrend id %s, but follow id requestd lock %s", currentLockInfo.ID, lockInfo.ID)
return &ConflictError{
StatusCode: http.StatusConflict,
}
}
if err := os.Remove(lockFilename); err != nil {
logger.Warnf("Can't delete file %s. Got follow error %v", lockFilename, err)
return err
}
return nil
}