Documentation
¶
Index ¶
- Variables
- type Config
- type Storage
- func (s *Storage) Delete(ctx context.Context, key string) error
- func (s *Storage) Exists(ctx context.Context, key string) bool
- func (s *Storage) List(ctx context.Context, prefix string, recursive bool) ([]string, error)
- func (s *Storage) Load(ctx context.Context, key string) ([]byte, error)
- func (s *Storage) Lock(ctx context.Context, key string) error
- func (s *Storage) Stat(ctx context.Context, key string) (certmagic.KeyInfo, error)
- func (s *Storage) Store(ctx context.Context, key string, value []byte) error
- func (s *Storage) Unlock(ctx context.Context, key string) error
Constants ¶
This section is empty.
Variables ¶
var ( // LockExpiration is the duration before which a Lock is considered expired LockExpiration = 1 * time.Minute // LockPollInterval is the interval between each check of the lock state. LockPollInterval = 1 * time.Second )
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// AEAD for Authenticated Encryption with Additional Data
AEAD tink.AEAD
// BucketName is the name of the OSS storage Bucket
BucketName string
// Region is the OSS region
Region string
// Endpoint is the OSS endpoint
Endpoint string
// AccessKeyID is the access key ID for OSS
AccessKeyID string
// AccessKeySecret is the access key secret for OSS
AccessKeySecret string
}
type Storage ¶
type Storage struct {
// contains filtered or unexported fields
}
Storage is a certmagic.Storage backed by an OSS bucket
func (*Storage) Delete ¶
Delete deletes key. An error should be returned only if the key still exists when the method returns.
func (*Storage) List ¶
List returns all keys that match prefix. If recursive is true, non-terminal keys will be enumerated (i.e. "directories" should be walked); otherwise, only keys prefixed exactly by prefix will be listed.
func (*Storage) Lock ¶
Lock acquires the lock for key, blocking until the lock can be obtained or an error is returned. Note that, even after acquiring a lock, an idempotent operation may have already been performed by another process that acquired the lock before - so always check to make sure idempotent operations still need to be performed after acquiring the lock.
The actual implementation of obtaining of a lock must be an atomic operation so that multiple Lock calls at the same time always results in only one caller receiving the lock at any given time.
To prevent deadlocks, all implementations (where this concern is relevant) should put a reasonable expiration on the lock in case Unlock is unable to be called due to some sort of network failure or system crash. Additionally, implementations should honor context cancellation as much as possible (in case the caller wishes to give up and free resources before the lock can be obtained).
IMPORTANT: TOCTOU race condition warning
When an expired lock is detected, this implementation performs a delete-then-reacquire sequence (HeadObject to check expiration, DeleteObject to remove the stale lock, then PutObject with ForbidOverwrite to create a new lock). Because OSS does not support atomic compare-and-swap (CAS) or conditional delete operations, there is a time-of-check-to-time-of-use (TOCTOU) race window between the delete and the subsequent put. In this window, another process could also detect the expired lock, delete it, and successfully acquire the lock — resulting in two processes believing they hold the lock simultaneously.
For single-instance Caddy deployments (the primary use case), this is acceptable because lock contention is between goroutines within the same process, and the ForbidOverwrite parameter provides sufficient atomicity for the initial acquisition. However, this implementation is NOT suitable for high-concurrency distributed scenarios where multiple independent processes compete for the same lock, as the TOCTOU window could lead to split-brain conditions.