azureblob

package
v1.8.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 27 Imported by: 0

README

Azure Blob Storage

Parity grade: C · SDK azure-sdk-for-go/sdk/storage/azblob@v1.8.0 · last audited 2026-09-03 (f1427114b)

Coverage

Metric Value
PARITY entries audited 19 (17 ok, 2 partial)
Feature families 5 (3 ok, 2 partial)
Known gaps 8
Deferred items 3
Resource leaks clean
Known gaps
  • Put Block / Put Block List (large-object multipart upload) is not implemented; Put Blob accepts only a single whole-body BlockBlob PUT.
  • Blob versions (x-ms-version-id, include=versions, version-scoped read/delete) and soft-delete (include=deleted, undelete) are not modeled.
  • No conditional-header support (If-Match/If-None-Match/If-Modified-Since/If-Unmodified-Since) on any operation.
  • Copy Blob is same-account and synchronous only; no cross-account or URL sources, no Abort Copy, no x-ms-copy-* properties on the destination.
  • No lease support, and Archive rehydration is instantaneous rather than asynchronous.
  • List Containers returns every container in one page; no prefix/marker/maxresults.
  • SharedKey signatures are not verified (families.auth), so stored access policies and SAS tokens never gate a request.
  • No append or page blobs, blob index tags, or Set/Get Blob Service Properties beyond the empty read.
Deferred
  • Initial implementation pass (2026-09-02): seeded this service from scratch per AZURE.md M0. No prior audit history to reconcile.
  • M0 review pass (2026-09-03): pkgs/azureauth, cli.go registration, and test/integration/azureblob_test.go all landed in the same PR as this service (see AZURE.md's M0 entry) -- the file was previously drafted assuming a multi-PR sequence that did not happen. sdk_module bumped to the azblob v1.8.0 actually pinned in go.mod (used by the integration test).
  • Dead-code sweep (2026-09-06, gopherstack-rxdr): removed errors.go's ErrInvalidBlobType/ErrInvalidRange sentinels -- unlike every other sentinel in this file (ErrContainerNotFound/ErrContainerAlreadyExists/ErrBlobNotFound, all returned by store.go and consumed via errors.Is at the handler.go boundary, matching the repo-wide convention seen in services/s3, services/sqs, services/dynamodb), these two were never returned by anything: putBlob's x-ms-blob-type check and getBlob's parseRange check are pure handler-local HTTP validation that never crosses into the backend layer. grep confirmed zero references anywhere in the repo outside their own declaration. TestPutBlob_RequiresBlockBlobType and TestGetBlob_RangeHeaderPartialRead/unsatisfiable assert on HTTP status/body strings, not the sentinels, and pass unchanged.

More

Documentation

Index

Constants

View Source
const (
	TierHot     = "Hot"
	TierCool    = "Cool"
	TierCold    = "Cold"
	TierArchive = "Archive"

	AccessBlob      = "blob"
	AccessContainer = "container"
)

Access tiers and public-access levels accepted by the REST API.

View Source
const (
	DeleteSnapshotsInclude = "include"
	DeleteSnapshotsOnly    = "only"
)

DeleteSnapshots values of the x-ms-delete-snapshots header.

View Source
const DefaultPort = 10000

DefaultPort is Azure Blob's fixed, protocol-conventional TCP port. This follows the same pattern as services/iot's MQTT broker (also a fixed, protocol-conventional default -- 1883 -- with a CLI/env override and no shared-pool fallback): pick one default and try to bind exactly that, rather than drawing from cli.go's shared --port-range-start/ --port-range-end PortAlloc pool (used for on-demand ephemeral resources like Lambda function URLs and ElastiCache, not fixed service ports) or inventing an alternative numbering scheme. The default value itself (10000) is Azurite's own Blob service port, so unmodified UseDevelopmentStorage=true-style SDK configuration works out of the box; see AZURE.md section 4 for the full rationale, including why this deliberately does NOT fall back into the shared PortAlloc pool if 10000 is taken (StartWorker fails fast instead -- see handler.go).

Variables

View Source
var (
	ErrContainerNotFound      = errors.New("azureblob: container not found")
	ErrContainerAlreadyExists = errors.New("azureblob: container already exists")
	ErrBlobNotFound           = errors.New("azureblob: blob not found")
	ErrSnapshotsPresent       = errors.New("azureblob: blob has snapshots")
	ErrInvalidMarker          = errors.New("azureblob: invalid list marker")

	// ErrSnapshotContainerNull and ErrSnapshotBlobNull are returned by
	// Restore when a snapshot's "containers" map (or a container's "Blobs"
	// map) holds a JSON null entry, which decodes to a nil pointer that
	// would panic on first dereference if stored as-is. See persistence.go.
	ErrSnapshotContainerNull = errors.New("azureblob: restore snapshot: container is null")
	ErrSnapshotBlobNull      = errors.New("azureblob: restore snapshot: blob is null")
)

Sentinel errors for Azure Blob Storage operations.

View Source
var ErrNilAppContext = errors.New("azureblob: nil app context")

ErrNilAppContext is returned when Init is called with a nil AppContext.

Functions

This section is empty.

Types

type BlobAttrs

type BlobAttrs struct {
	Metadata           map[string]string
	ContentType        string
	ContentEncoding    string
	ContentLanguage    string
	ContentDisposition string
	CacheControl       string
	ContentMD5         string
	AccessTier         string
}

BlobAttrs are the client-settable properties of a blob.

type BlobInfo

type BlobInfo struct {
	LastModified       time.Time
	AccessTierChanged  time.Time
	Metadata           map[string]string
	Name               string
	ContentType        string
	ContentEncoding    string
	ContentLanguage    string
	ContentDisposition string
	CacheControl       string
	ContentMD5         string
	ETag               string
	AccessTier         string
	Snapshot           string
	ContentLength      int64
	AccessTierInferred bool
}

BlobInfo is a read-only snapshot of a blob (or of one of its snapshots), returned by the StorageBackend blob accessors.

type BlobPage

type BlobPage struct {
	NextMarker string
	Entries    []ListEntry
}

BlobPage is one page of a List Blobs response.

type ConfigProvider

type ConfigProvider interface {
	GetAzureBlobSettings() Settings
}

ConfigProvider is a private interface to extract AzureBlob configuration from the abstract AppContext Config, mirroring services/s3.ConfigProvider.

type ContainerInfo

type ContainerInfo struct {
	CreatedAt    time.Time
	LastModified time.Time
	Metadata     map[string]string
	Name         string
	ETag         string
	PublicAccess string
	ACL          []SignedIdentifier
}

ContainerInfo is a read-only snapshot of a container, returned by the StorageBackend container accessors. It carries no reference to backend storage.

type ContainerOptions

type ContainerOptions struct {
	Metadata     map[string]string
	PublicAccess string
}

ContainerOptions are the create-time settings of a container.

type Handler

type Handler struct {
	Backend StorageBackend

	// Endpoint is e.g. "http://127.0.0.1:10000" -- used to build
	// ServiceEndpoint in list responses.
	Endpoint string
	// Port is the TCP port StartWorker binds. Set from Settings at Init time
	// (see provider.go); defaults to DefaultPort. Unlike a per-resource
	// ephemeral allocation, this is a single fixed, protocol-conventional
	// port (mirroring services/iot's MQTT broker) -- there is no fallback
	// pool, so StartWorker fails fast if it's unavailable rather than
	// silently binding a different port.
	Port int
	// EnforcePublicAccess makes anonymous requests honour container
	// public-access levels; off, anonymous access is unrestricted.
	EnforcePublicAccess bool
	// contains filtered or unexported fields
}

Handler is the Echo HTTP handler for Azure Blob Storage operations.

func NewHandler

func NewHandler(backend StorageBackend) *Handler

NewHandler creates a new Azure Blob Handler. Port defaults to DefaultPort; callers (typically provider.go) override it from Settings.

func (*Handler) ExtractOperation

func (h *Handler) ExtractOperation(c *echo.Context) string

ExtractOperation extracts the Azure Blob operation name from the request, for metrics labeling.

func (*Handler) ExtractResource

func (h *Handler) ExtractResource(c *echo.Context) string

ExtractResource extracts the container/blob resource identifier from the request path, for metrics labeling.

func (*Handler) GetSupportedOperations

func (h *Handler) GetSupportedOperations() []string

GetSupportedOperations returns the list of supported Azure Blob operations.

func (*Handler) Handler

func (h *Handler) Handler() echo.HandlerFunc

Handler returns the Echo handler function for Azure Blob operations.

func (*Handler) MatchPriority

func (h *Handler) MatchPriority() int

MatchPriority returns the routing priority for the AzureBlob handler. Irrelevant in practice since RouteMatcher never matches; 0 (lowest) is the safe default.

func (*Handler) Name

func (h *Handler) Name() string

Name returns the service name.

func (*Handler) Reset

func (h *Handler) Reset()

Reset clears all in-memory state from the backend. It is used by the POST /_gopherstack/reset endpoint for CI pipelines and rapid local development.

func (*Handler) Restore

func (h *Handler) Restore(ctx context.Context, data []byte) error

Restore implements persistence.Persistable by delegating to the backend.

func (*Handler) RouteMatcher

func (h *Handler) RouteMatcher() service.Matcher

RouteMatcher exists only to satisfy service.Registerable's interface contract: AzureBlob deliberately never matches on the shared AWS single-port Router. It runs on its own dedicated listener started by StartWorker (see provider.go for the full rationale). AzureBlob's Provider IS registered in cli.go's getMostRecentServiceProviders like every other service -- startBackgroundWorkers calls StartWorker via the service.BackgroundWorker interface regardless of routing, which is how the dedicated listener comes up. Only RouteMatcher itself is inert, kept so *Handler satisfies service.Registerable.

func (*Handler) Shutdown

func (h *Handler) Shutdown(ctx context.Context)

Shutdown stops the dedicated Blob listener. A graceful Shutdown error (e.g. its context expiring before active connections finish) is logged and followed by Close, which forcibly closes the listener and any remaining idle/active connections; any Close error is logged too rather than leaving the listener to leak silently.

func (*Handler) Snapshot

func (h *Handler) Snapshot(ctx context.Context) []byte

Snapshot implements persistence.Persistable by delegating to the backend.

func (*Handler) StartWorker

func (h *Handler) StartWorker(ctx context.Context) error

StartWorker binds the dedicated Blob listener and starts serving on it. See provider.go's Provider doc comment for why AzureBlob needs its own listener instead of registering into the shared AWS Router.

Binding is synchronous: net.Listen returns before StartWorker does, so a bind failure is returned to the caller directly instead of only being logged from the background goroutine after startup has already reported success. This mirrors services/iot's MQTT broker (services/iot/broker.go), gopherstack's existing precedent for a service with a fixed, protocol-conventional default port: bind exactly the configured port (h.Port, from Settings -- see settings.go/provider.go) and fail fast if that's unavailable, rather than silently falling back into the shared --port-range-start/--port-range-end PortAlloc pool used for on-demand ephemeral resources elsewhere (Lambda function URLs, ElastiCache). A fallback there would be just as surprising as picking a different default port number outright: either way, an SDK relying on the well-known default (UseDevelopmentStorage=true-style config) would silently end up talking to the wrong port. Failing fast surfaces the conflict instead.

type InMemoryBackend

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

InMemoryBackend implements StorageBackend using in-memory maps guarded by a single RWMutex. Shaped after services/sqs's InMemoryBackend, but simpler: Azure Blob's MVP surface (see AZURE.md/PARITY.md) has no janitor, no metrics emitter, and no cross-resource relationships to track, so a single coarse lock over one map of containers is sufficient.

func NewInMemoryBackend

func NewInMemoryBackend() *InMemoryBackend

NewInMemoryBackend creates a new empty InMemoryBackend.

func (*InMemoryBackend) CopyBlob

func (b *InMemoryBackend) CopyBlob(
	srcContainer, srcBlob, srcSnapshot, dstContainer, dstBlob string, md map[string]string,
) (BlobInfo, error)

CopyBlob copies a blob (or snapshot) within the account. A nil md inherits the source metadata.

func (*InMemoryBackend) CreateContainer

func (b *InMemoryBackend) CreateContainer(name string) error

CreateContainer creates a new, empty container with default settings.

func (*InMemoryBackend) CreateContainerWith

func (b *InMemoryBackend) CreateContainerWith(name string, opts ContainerOptions) (ContainerInfo, error)

CreateContainerWith creates a container with metadata and a public-access level. Returns ErrContainerAlreadyExists if the name is taken.

func (*InMemoryBackend) DeleteBlob

func (b *InMemoryBackend) DeleteBlob(container, blob string) error

DeleteBlob removes a blob that has no snapshots.

func (*InMemoryBackend) DeleteBlobAt

func (b *InMemoryBackend) DeleteBlobAt(container, blob, snapshot, mode string) error

DeleteBlobAt deletes a snapshot (snapshot != ""), or the base blob subject to the x-ms-delete-snapshots mode: with snapshots present, "" fails with ErrSnapshotsPresent, "include" removes all, "only" removes just snapshots.

func (*InMemoryBackend) DeleteContainer

func (b *InMemoryBackend) DeleteContainer(name string) error

DeleteContainer removes a container and all of its blobs. Returns ErrContainerNotFound if the container does not exist.

func (*InMemoryBackend) GetBlob

func (b *InMemoryBackend) GetBlob(container, blob string) (BlobInfo, []byte, error)

GetBlob returns a blob's metadata and full body; the slice is immutable backend storage and must not be modified.

func (*InMemoryBackend) GetBlobAt

func (b *InMemoryBackend) GetBlobAt(container, blob, snapshot string) (BlobInfo, []byte, error)

GetBlobAt is GetBlob for the base blob (snapshot == "") or one snapshot.

func (*InMemoryBackend) GetContainer

func (b *InMemoryBackend) GetContainer(name string) (ContainerInfo, error)

GetContainer returns a container's properties, metadata and ACL.

func (*InMemoryBackend) HeadBlob

func (b *InMemoryBackend) HeadBlob(container, blob string) (BlobInfo, error)

HeadBlob returns a blob's metadata without its body.

func (*InMemoryBackend) HeadBlobAt

func (b *InMemoryBackend) HeadBlobAt(container, blob, snapshot string) (BlobInfo, error)

HeadBlobAt is HeadBlob for the base blob (snapshot == "") or one snapshot.

func (*InMemoryBackend) ListBlobs

func (b *InMemoryBackend) ListBlobs(container string) ([]BlobInfo, error)

ListBlobs returns every base blob in container, sorted by name. Returns ErrContainerNotFound if the container does not exist.

func (*InMemoryBackend) ListBlobsPage

func (b *InMemoryBackend) ListBlobsPage(container string, opts ListBlobsOptions) (BlobPage, error)

ListBlobsPage lists blobs with prefix/delimiter collapsing and marker paging. Returns ErrContainerNotFound or ErrInvalidMarker.

func (*InMemoryBackend) ListContainers

func (b *InMemoryBackend) ListContainers() []ContainerInfo

ListContainers returns a snapshot of all containers, sorted by name (the order Azure's List Containers returns them in).

func (*InMemoryBackend) PutBlob

func (b *InMemoryBackend) PutBlob(container, blob string, data []byte, contentType string) (BlobInfo, error)

PutBlob stores data as a block blob with only a content type set.

func (*InMemoryBackend) PutBlobWith

func (b *InMemoryBackend) PutBlobWith(container, blob string, data []byte, attrs BlobAttrs) (BlobInfo, error)

PutBlobWith stores data as a block blob named blob within container, replacing any existing blob but keeping its snapshots, as Azure does. Returns ErrContainerNotFound if the container does not exist.

func (*InMemoryBackend) Reset

func (b *InMemoryBackend) Reset()

Reset clears all in-memory state. It is used by the POST /_gopherstack/reset endpoint for CI pipelines and rapid local development.

func (*InMemoryBackend) Restore

func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error

Restore loads backend state from a JSON snapshot. It implements persistence.Persistable.

func (*InMemoryBackend) SetBlobMetadata

func (b *InMemoryBackend) SetBlobMetadata(container, blob string, md map[string]string) (BlobInfo, error)

SetBlobMetadata replaces a blob's metadata.

func (*InMemoryBackend) SetBlobProperties

func (b *InMemoryBackend) SetBlobProperties(container, blob string, attrs BlobAttrs) (BlobInfo, error)

SetBlobProperties replaces the content properties of a blob. Properties absent from attrs are cleared, as in Set Blob Properties.

func (*InMemoryBackend) SetBlobTier

func (b *InMemoryBackend) SetBlobTier(container, blob, tier string) (string, BlobInfo, error)

SetBlobTier sets the access tier and returns the tier it replaced.

func (*InMemoryBackend) SetContainerACL

func (b *InMemoryBackend) SetContainerACL(name, publicAccess string, ids []SignedIdentifier) (ContainerInfo, error)

SetContainerACL replaces a container's public-access level and stored access policies.

func (*InMemoryBackend) SetContainerMetadata

func (b *InMemoryBackend) SetContainerMetadata(name string, md map[string]string) (ContainerInfo, error)

SetContainerMetadata replaces a container's metadata.

func (*InMemoryBackend) Snapshot

func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte

Snapshot serialises the backend state to JSON. It implements persistence.Persistable.

func (*InMemoryBackend) SnapshotBlob

func (b *InMemoryBackend) SnapshotBlob(container, blob string, md map[string]string) (BlobInfo, error)

SnapshotBlob freezes a copy of the blob. A nil md inherits the blob's metadata; a non-nil md (even empty) replaces it.

type ListBlobsOptions

type ListBlobsOptions struct {
	Prefix           string
	Delimiter        string
	Marker           string
	MaxResults       int
	IncludeSnapshots bool
}

ListBlobsOptions are the List Blobs query parameters.

type ListEntry

type ListEntry struct {
	Prefix string
	Blob   BlobInfo
	// IsPrefix selects Prefix over Blob.
	IsPrefix bool
}

ListEntry is one item of a List Blobs page: a blob or a BlobPrefix.

type Provider

type Provider struct{}

Provider implements service.Provider for the Azure Blob Storage service.

Unlike every other provider in this repo, AzureBlob does not register a RouteMatcher into the shared AWS single-port Router: Azure Blob's path shape (/<account>/<container>/<blob>) has no service-identifying header the way AWS's X-Amz-Target does, so multiplexing it onto the shared port risks exactly the collision the router avoids by construction for AWS services (see AZURE.md section 4). Instead the returned Handler implements service.BackgroundWorker and stands up its own dedicated *echo.Echo/ *http.Server, listening on a fixed, protocol-conventional port -- the same pattern services/iot's MQTT broker already uses in this repo for a well-known port (1883) that isn't part of the shared AWS request/response cycle. It is registered in cli.go's getMostRecentServiceProviders like every other provider; only its RouteMatcher (which always returns false) is inert.

func (*Provider) Init

Init initializes the AzureBlob service backend and handler. The configured port (Settings.Port, default DefaultPort) is only recorded here; the actual TCP bind happens synchronously in Handler.StartWorker, so a port-in-use failure is returned to the caller directly instead of being discovered later from a background goroutine.

func (*Provider) Name

func (p *Provider) Name() string

Name returns the service provider name.

type Settings

type Settings struct {
	// Port is the fixed TCP port for the dedicated Blob listener. See
	// handler.go's StartWorker for what happens when it's unavailable
	// (fails fast; no fallback pool, matching services/iot's MQTT broker).
	Port int `` //nolint:lll // config struct tags are intentionally verbose
	/* 176-byte string literal not displayed */
	// EnforcePublicAccess rejects anonymous requests the container's public-access level does not allow.
	EnforcePublicAccess bool `` //nolint:lll // config struct tags are intentionally verbose
	/* 247-byte string literal not displayed */
}

Settings holds service-level configuration for the Azure Blob backend. Fields are picked up by the Kong CLI parser when this struct is embedded in the root CLI command (see cli.go's CLI.AzureBlob field), mirroring services/s3's Settings pattern.

func DefaultSettings

func DefaultSettings() Settings

DefaultSettings returns the default Settings. Used when no ConfigProvider is available at init time (e.g. tests constructing a Provider directly).

type SignedIdentifier

type SignedIdentifier struct {
	ID         string
	Start      string
	Expiry     string
	Permission string
}

SignedIdentifier is one stored access policy of a container ACL.

type StorageBackend

type StorageBackend interface {
	CreateContainerWith(name string, opts ContainerOptions) (ContainerInfo, error)
	DeleteContainer(name string) error
	ListContainers() []ContainerInfo
	GetContainer(name string) (ContainerInfo, error)
	SetContainerMetadata(name string, md map[string]string) (ContainerInfo, error)
	SetContainerACL(name, publicAccess string, ids []SignedIdentifier) (ContainerInfo, error)

	PutBlobWith(container, blob string, data []byte, attrs BlobAttrs) (BlobInfo, error)
	GetBlobAt(container, blob, snapshot string) (BlobInfo, []byte, error)
	HeadBlobAt(container, blob, snapshot string) (BlobInfo, error)
	DeleteBlobAt(container, blob, snapshot, mode string) error
	ListBlobsPage(container string, opts ListBlobsOptions) (BlobPage, error)
	SetBlobProperties(container, blob string, attrs BlobAttrs) (BlobInfo, error)
	SetBlobMetadata(container, blob string, md map[string]string) (BlobInfo, error)
	SetBlobTier(container, blob, tier string) (string, BlobInfo, error)
	SnapshotBlob(container, blob string, md map[string]string) (BlobInfo, error)
	CopyBlob(srcContainer, srcBlob, srcSnapshot, dstContainer, dstBlob string, md map[string]string) (BlobInfo, error)

	// Reset clears all in-memory state. Used by the
	// POST /_gopherstack/reset endpoint for CI pipelines and rapid local development.
	Reset()
}

StorageBackend defines the interface for an Azure Blob Storage backend. Shaped after services/sqs's StorageBackend: a narrow, testable seam between the wire handler and storage, so handler tests can substitute a fake.

Jump to

Keyboard shortcuts

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