-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathstorage.go
104 lines (90 loc) · 3.47 KB
/
storage.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
package goacmedns
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
)
// Storage is an interface describing the required functions for an ACME DNS
// Account storage mechanism.
type Storage interface {
// Save will persist the `Account` data that has been `Put` so far
Save() error
// Put will add an `Account` for the given domain to the storage. It may not
// be persisted until `Save` is called.
Put(string, Account) error
// Fetch will retrieve an `Account` for the given domain from the storage. If
// the provided domain does not have an `Account` saved in the storage
// `ErrDomainNotFound` will be returned
Fetch(string) (Account, error)
// FetchAll retrieves all the `Account` objects from the storage and
// returns a map that has domain names as its keys and `Account` objects
// as values.
FetchAll() map[string]Account
}
// ErrDomainNotFound is returned from `Fetch` when the provided domain is not
// present in the storage.
var ErrDomainNotFound = errors.New("requested domain is not present in storage")
// fileStorage implements the `Storage` interface and persists `Accounts` to
// a JSON file on disk.
type fileStorage struct {
// path is the filepath that the `accounts` are persisted to when the `Save`
// function is called.
path string
// mode is the file mode used when the `path` JSON file must be created
mode os.FileMode
// accounts holds the `Account` data that has been `Put` into the storage
accounts map[string]Account
}
// NewFileStorage returns a `Storage` implementation backed by JSON content
// saved into the provided `path` on disk. The file at `path` will be created if
// required. When creating a new file the provided `mode` is used to set the
// permissions.
func NewFileStorage(path string, mode os.FileMode) Storage {
fs := fileStorage{
path: path,
mode: mode,
accounts: make(map[string]Account),
}
// Opportunistically try to load the account data. Return an empty account if
// any errors occur.
if jsonData, err := ioutil.ReadFile(path); err == nil {
if err := json.Unmarshal(jsonData, &fs.accounts); err != nil {
return fs
}
}
return fs
}
// Save persists the `Account` data to the fileStorage's configured path. The
// file at that path will be created with the fileStorage's mode if required.
func (f fileStorage) Save() error {
if serialized, err := json.Marshal(f.accounts); err != nil {
return fmt.Errorf("Failed to marshal account: %w", err)
} else if err = ioutil.WriteFile(f.path, serialized, f.mode); err != nil {
return fmt.Errorf("Failed to write storage file: %w", err)
}
return nil
}
// Put saves an `Account` for the given `Domain` into the in-memory accounts of
// the fileStorage instance. The `Account` data will not be written to disk
// until the `Save` function is called.
func (f fileStorage) Put(domain string, acct Account) error {
f.accounts[domain] = acct
return nil
}
// Fetch retrieves the `Account` object for the given `domain` from the
// fileStorage in-memory accounts. If the `domain` provided does not have an
// `Account` in the storage an `ErrDomainNotFound` error is returned.
func (f fileStorage) Fetch(domain string) (Account, error) {
if acct, exists := f.accounts[domain]; exists {
return acct, nil
}
return Account{}, ErrDomainNotFound
}
// FetchAll retrieves all the `Account` objects from the fileStorage and
// returns a map that has domain names as its keys and `Account` objects
// as values.
func (f fileStorage) FetchAll() map[string]Account {
return f.accounts
}