| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package framework |
| |
| import ( |
| "context" |
| "encoding/json" |
| "strings" |
| "time" |
| |
| uuid "github.com/hashicorp/go-uuid" |
| "github.com/hashicorp/vault/sdk/helper/jsonutil" |
| "github.com/hashicorp/vault/sdk/logical" |
| ) |
| |
| // WALPrefix is the prefix within Storage where WAL entries will be written. |
| const WALPrefix = "wal/" |
| |
| type WALEntry struct { |
| ID string `json:"-"` |
| Kind string `json:"type"` |
| Data interface{} `json:"data"` |
| CreatedAt int64 `json:"created_at"` |
| } |
| |
| // PutWAL writes some data to the WAL. |
| // |
| // The kind parameter is used by the framework to allow users to store |
| // multiple kinds of WAL data and to easily disambiguate what data they're |
| // expecting. |
| // |
| // Data within the WAL that is uncommitted (CommitWAL hasn't be called) |
| // will be given to the rollback callback when an rollback operation is |
| // received, allowing the backend to clean up some partial states. |
| // |
| // The data must be JSON encodable. |
| // |
| // This returns a unique ID that can be used to reference this WAL data. |
| // WAL data cannot be modified. You can only add to the WAL and commit existing |
| // WAL entries. |
| func PutWAL(ctx context.Context, s logical.Storage, kind string, data interface{}) (string, error) { |
| value, err := json.Marshal(&WALEntry{ |
| Kind: kind, |
| Data: data, |
| CreatedAt: time.Now().UTC().Unix(), |
| }) |
| if err != nil { |
| return "", err |
| } |
| |
| id, err := uuid.GenerateUUID() |
| if err != nil { |
| return "", err |
| } |
| |
| return id, s.Put(ctx, &logical.StorageEntry{ |
| Key: WALPrefix + id, |
| Value: value, |
| }) |
| } |
| |
| // GetWAL reads a specific entry from the WAL. If the entry doesn't exist, |
| // then nil value is returned. |
| // |
| // The kind, value, and error are returned. |
| func GetWAL(ctx context.Context, s logical.Storage, id string) (*WALEntry, error) { |
| entry, err := s.Get(ctx, WALPrefix+id) |
| if err != nil { |
| return nil, err |
| } |
| if entry == nil { |
| return nil, nil |
| } |
| |
| var raw WALEntry |
| if err := jsonutil.DecodeJSON(entry.Value, &raw); err != nil { |
| return nil, err |
| } |
| raw.ID = id |
| |
| return &raw, nil |
| } |
| |
| // DeleteWAL commits the WAL entry with the given ID. Once committed, |
| // it is assumed that the operation was a success and doesn't need to |
| // be rolled back. |
| func DeleteWAL(ctx context.Context, s logical.Storage, id string) error { |
| return s.Delete(ctx, WALPrefix+id) |
| } |
| |
| // ListWAL lists all the entries in the WAL. |
| func ListWAL(ctx context.Context, s logical.Storage) ([]string, error) { |
| keys, err := s.List(ctx, WALPrefix) |
| if err != nil { |
| return nil, err |
| } |
| |
| for i, k := range keys { |
| keys[i] = strings.TrimPrefix(k, WALPrefix) |
| } |
| |
| return keys, nil |
| } |