objectstore

package
v0.14.0-rc.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package objectstore is the production controlplane.ObjectStoreFactory: a small S3-compatible client for the ONE thing Burrow does with object storage, which is own the seam between a bucket and the backups written to it (ADR-0063).

Its surface is the whole of what ADR-0063 §2 permits and no more — create the bucket Burrow will own, write and delete a probe object, read the lifecycle configuration that must be reconciled against backup retention, and check a bucket is reachable. There is no cp, no sync, no listing of arbitrary prefixes, no presigned URL, and no policy/IAM/replication surface. That is not an omission to be filled in later: every vendor ships a capable CLI for those, and a capability enters here only when a Burrow feature requires it.

The client is a net/http client with an in-tree SigV4 signer (sigv4.go) rather than a vendor SDK, for the same reason the DNS adapters are: five API calls do not justify an SDK and its transitive module set (CLAUDE.md). Requests are PATH-STYLE (https://endpoint/bucket/key), which is what the S3-compatible vendors accept in common.

It lives under controlplane/ (not controlplane/internal) so cmd/burrowd and the managed module can wire it; it is licensed Apache-2.0.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Factory

type Factory struct {
	// contains filtered or unexported fields
}

Factory builds a Store for an endpoint and credential pair (ADR-0063 §1). The vendor is whoever answers the endpoint: there is no vendor list here, deliberately.

func NewFactory

func NewFactory() *Factory

NewFactory returns a Factory bounding each object-store call by controlplane.ObjectStoreCallTimeout. The bound is declared there rather than here because registering a provider makes several of these calls in one API request, and the client waiting on that request sizes itself from the same constant (issue #404).

func (*Factory) ObjectStore

func (f *Factory) ObjectStore(endpoint, region string, cred controlplane.ObjectStoreCredential) (controlplane.ObjectStore, error)

ObjectStore returns a client for the bucket-holding endpoint, signing with cred.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is one endpoint's S3-compatible client, holding one credential pair.

func (*Store) BucketExists

func (s *Store) BucketExists(ctx context.Context, bucket string) (bool, error)

BucketExists reports whether the bucket is present AND reachable with this credential. A bucket that answers 403 is reported as absent: it may exist and belong to someone else, and either way Burrow can do nothing with it, so treating it as present would be the more dangerous answer.

func (*Store) CreateBucket

func (s *Store) CreateBucket(ctx context.Context, bucket string) error

CreateBucket creates the bucket under exactly the name it is given — the engine owns the name (ADR-0063 §4), and this adapter never derives, adopts, or falls back to another one.

func (*Store) DeleteObject

func (s *Store) DeleteObject(ctx context.Context, bucket, key string) error

DeleteObject removes key. S3 answers 204 whether or not the object was there, so deleting an absent object is a no-op rather than an error.

func (*Store) LifecycleRules

func (s *Store) LifecycleRules(ctx context.Context, bucket string) ([]controlplane.LifecycleRule, error)

LifecycleRules returns the bucket's lifecycle rules, or an error wrapping ErrLifecycleUnknown where the answer cannot be had — the vendor does not serve the lifecycle API (501/405), or this credential is not permitted to read it (403). That distinction is load-bearing: ADR-0063 §3 wants an unreadable configuration reported as UNKNOWN, never as verified, and an adapter that quietly returned "no rules" for a 403 would report the second as the first.

func (*Store) PutObject

func (s *Store) PutObject(ctx context.Context, bucket, key string, body []byte) error

PutObject writes body at key.

func (*Store) PutObjectStream

func (s *Store) PutObjectStream(ctx context.Context, bucket, key string, body io.Reader, size int64, sha256Hex string) (bool, error)

PutObjectStream writes size bytes read from body at key, streaming rather than buffering: the one payload Burrow puts here that is not small is a pg_dump, and a container that held a multi-gigabyte dump in memory to write it would be OOM-killed by the limit that protects the rest of the node.

sha256Hex is the hex SHA-256 of exactly those bytes, computed by the caller in a pass over the same file. It goes into x-amz-content-sha256, which the endpoint VALIDATES the body against and rejects on a mismatch — so a truncated or corrupted transfer is refused by the vendor rather than being stored as a backup that will not restore. That is the first of the two facts ADR-0063 §7 needs; the second is StatObject reading the object back afterwards.

refused distinguishes "the endpoint answered and said no" from "the write did not complete". Only the second is worth retrying. A 403, a 404 on the bucket, or a malformed request will answer the same way however many times it is asked, and spending the retry budget on it only delays the loud failure. A 429 or a 5xx is the endpoint asking to be asked again, so those are NOT refusals.

func (*Store) StatObject

func (s *Store) StatObject(ctx context.Context, bucket, key string) (int64, error)

StatObject returns the size of the object at key, or ErrNotFound when it is not there.

This is the read-back that lets a Backup row claim the object is retrievable rather than merely accepted (ADR-0063 §7). It is a HEAD and not a full GET on purpose: the body's integrity is already established by the signed payload hash the write was validated against, so re-reading gigabytes would double the transfer and the egress bill to learn nothing the PUT did not already prove. What a HEAD adds — and the PUT cannot — is that the object is VISIBLE at the key Burrow recorded, with the length Burrow sent, to a subsequent request.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL