generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 5
/
api.go
116 lines (104 loc) 路 3.43 KB
/
api.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
// Package configuration is the FTL configuration and secret management API.
//
// The full design is documented [here].
//
// A [Manager] is the high-level interface to storing, listing, and retrieving
// secrets and configuration. A [Resolver] is the next layer, mapping
// names to a storage location key such as environment variables, keychain, etc.
// The [Provider] is the final layer, responsible for actually storing and
// retrieving values in concrete storage.
//
// A constructed [Manager] and its providers are parametric on either secrets or
// configuration and thus cannot be used interchangeably.
//
// [here]: https://hackmd.io/@ftl/S1e6YVEuq6
package configuration
import (
"context"
"errors"
"net/url"
"strings"
"github.com/alecthomas/types/optional"
)
// ErrNotFound is returned when a configuration entry is not found or cannot be resolved.
var ErrNotFound = errors.New("not found")
// Entry in the configuration store.
type Entry struct {
Ref
Accessor *url.URL
}
// A Ref is a reference to a configuration value.
type Ref struct {
Module optional.Option[string]
Name string
}
// NewRef creates a new Ref.
//
// If [module] is empty, the Ref is considered to be a global configuration value.
func NewRef(module, name string) Ref {
return Ref{Module: optional.Zero(module), Name: name}
}
func ParseRef(s string) (Ref, error) {
ref := Ref{}
err := ref.UnmarshalText([]byte(s))
return ref, err
}
func (k Ref) String() string {
if m, ok := k.Module.Get(); ok {
return m + "." + k.Name
}
return k.Name
}
func (k *Ref) UnmarshalText(text []byte) error {
s := string(text)
if i := strings.Index(s, "."); i != -1 {
k.Module = optional.Some(s[:i])
k.Name = s[i+1:]
} else {
k.Name = s
}
return nil
}
// A Resolver resolves configuration names to keys that are then used to load
// values from a Provider.
//
// This indirection allows for the storage of configuration values to be
// abstracted from the configuration itself. For example, the ftl-project.toml
// file contains per-module and global configuration maps, but the secrets
// themselves may be stored in a separate secret store such as a system keychain.
type Resolver[R Role] interface {
Role() R
Get(ctx context.Context, ref Ref) (key *url.URL, err error)
Set(ctx context.Context, ref Ref, key *url.URL) error
Unset(ctx context.Context, ref Ref) error
List(ctx context.Context) ([]Entry, error)
}
// Provider is a generic interface for storing and retrieving configuration and secrets.
type Provider[R Role] interface {
Role() R
Key() string
Load(ctx context.Context, ref Ref, key *url.URL) ([]byte, error)
}
// A MutableProvider is a Provider that can update configuration.
type MutableProvider[R Role] interface {
Provider[R]
// Writer returns true if this provider should be used to store configuration.
//
// Only one provider should return true.
//
// To be usable from the CLI, each provider must be a Kong-compatible struct
// containing a flag that this method should return. For example:
//
// type InlineProvider struct {
// Inline bool `help:"Write values inline." group:"Provider:" xor:"configwriter"`
// }
//
// func (i InlineProvider) Writer() bool { return i.Inline }
//
// The "xor" tag is used to ensure that only one writer is selected.
Writer() bool
// Store a configuration value and return its key.
Store(ctx context.Context, ref Ref, value []byte) (*url.URL, error)
// Delete a configuration value.
Delete(ctx context.Context, ref Ref) error
}