1package framework 2 3import ( 4 "context" 5 "encoding/json" 6 "strings" 7 "time" 8 9 uuid "github.com/hashicorp/go-uuid" 10 "github.com/hashicorp/vault/sdk/helper/jsonutil" 11 "github.com/hashicorp/vault/sdk/logical" 12) 13 14// WALPrefix is the prefix within Storage where WAL entries will be written. 15const WALPrefix = "wal/" 16 17type WALEntry struct { 18 ID string `json:"-"` 19 Kind string `json:"type"` 20 Data interface{} `json:"data"` 21 CreatedAt int64 `json:"created_at"` 22} 23 24// PutWAL writes some data to the WAL. 25// 26// The kind parameter is used by the framework to allow users to store 27// multiple kinds of WAL data and to easily disambiguate what data they're 28// expecting. 29// 30// Data within the WAL that is uncommitted (CommitWAL hasn't be called) 31// will be given to the rollback callback when an rollback operation is 32// received, allowing the backend to clean up some partial states. 33// 34// The data must be JSON encodable. 35// 36// This returns a unique ID that can be used to reference this WAL data. 37// WAL data cannot be modified. You can only add to the WAL and commit existing 38// WAL entries. 39func PutWAL(ctx context.Context, s logical.Storage, kind string, data interface{}) (string, error) { 40 value, err := json.Marshal(&WALEntry{ 41 Kind: kind, 42 Data: data, 43 CreatedAt: time.Now().UTC().Unix(), 44 }) 45 if err != nil { 46 return "", err 47 } 48 49 id, err := uuid.GenerateUUID() 50 if err != nil { 51 return "", err 52 } 53 54 return id, s.Put(ctx, &logical.StorageEntry{ 55 Key: WALPrefix + id, 56 Value: value, 57 }) 58} 59 60// GetWAL reads a specific entry from the WAL. If the entry doesn't exist, 61// then nil value is returned. 62// 63// The kind, value, and error are returned. 64func GetWAL(ctx context.Context, s logical.Storage, id string) (*WALEntry, error) { 65 entry, err := s.Get(ctx, WALPrefix+id) 66 if err != nil { 67 return nil, err 68 } 69 if entry == nil { 70 return nil, nil 71 } 72 73 var raw WALEntry 74 if err := jsonutil.DecodeJSON(entry.Value, &raw); err != nil { 75 return nil, err 76 } 77 raw.ID = id 78 79 return &raw, nil 80} 81 82// DeleteWAL commits the WAL entry with the given ID. Once committed, 83// it is assumed that the operation was a success and doesn't need to 84// be rolled back. 85func DeleteWAL(ctx context.Context, s logical.Storage, id string) error { 86 return s.Delete(ctx, WALPrefix+id) 87} 88 89// ListWAL lists all the entries in the WAL. 90func ListWAL(ctx context.Context, s logical.Storage) ([]string, error) { 91 keys, err := s.List(ctx, WALPrefix) 92 if err != nil { 93 return nil, err 94 } 95 96 for i, k := range keys { 97 keys[i] = strings.TrimPrefix(k, WALPrefix) 98 } 99 100 return keys, nil 101} 102