fs

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

README

fs Go Reference codecov experimental

Simple S3-compatible storage server for development and testing.

Features

S3-Compatible Storage Server

A lightweight S3-compatible storage server for development and testing.

Quick Start:

# Install
go install github.com/go-faster/fs/cmd/fs@latest

# Start the server
fs s3

# Or with custom configuration
fs s3 --addr :9000 --root /data/s3

Features:

  • Bucket operations (create, delete, list)
  • Object operations (put, get, delete, list, copy, tagging, metadata)
  • Multipart uploads
  • File system-based storage
  • Compatible with AWS CLI, MinIO client, and other S3 clients
  • Health check endpoint

Compatibility is measured against the upstream ceph/s3-tests suite and real S3 clients — see the S3 conformance report.

Example Usage:

# Using AWS CLI
export AWS_ENDPOINT_URL=http://localhost:8080
aws s3 mb s3://mybucket --endpoint-url $AWS_ENDPOINT_URL
aws s3 cp file.txt s3://mybucket/ --endpoint-url $AWS_ENDPOINT_URL

# Using cURL
curl -X PUT http://localhost:8080/mybucket
curl -X PUT -d "Hello!" http://localhost:8080/mybucket/hello.txt
curl http://localhost:8080/mybucket/hello.txt

Installation

go install github.com/go-faster/fs/cmd/fs@latest

Or build from source:

git clone https://github.com/go-faster/fs
cd fs
go build -o bin/fs ./cmd/fs

Usage

Quick Start
# Start S3 server with defaults
fs s3

# Show help
fs s3 --help
Configuration

The server supports both YAML configuration files and command-line flags:

# Using YAML configuration
fs s3 --config config.yaml

# Using command-line flags
fs s3 --addr :9000 --root /var/lib/s3data

# Mix both (flags override config file)
fs s3 --config config.yaml --addr :9000

# Generate example configuration
fs s3 --generate-config > my-config.yaml

Run fs s3 --generate-config to produce a fully commented configuration template, and fs s3 --help for the list of flags.

Example Configuration
server:
  addr: ":8080"
  read_timeout: 30s
  write_timeout: 30s
  idle_timeout: 120s
  health_path: "/health"

storage:
  root: ".s3data"
  type: "filesystem"

observability:
  service_name: "go-faster/fs"
  enable_request_logging: true
  enable_metrics: true
  enable_tracing: true

Use as a library

The S3 server is embeddable. Install the module and pick a storage backend — storagefs for the filesystem, storagemem for in-memory, or your own implementation of the fs.Storage interface:

go get github.com/go-faster/fs

The library core pulls in no observability stack — wrap the handler yourself (e.g. with otelhttp) via server.NewHandler or Config.WrapHandler.

Custom backends can verify themselves against the storage contract with the storagetest conformance suite:

func TestStorage(t *testing.T) {
	storagetest.Run(t, func(t testing.TB) fs.Storage {
		return mybackend.New(t.TempDir())
	})
}
Mount the handler into your own server

Use server.NewHandler when you already run an http.Server or mux and just want to expose the S3 API (optionally under a path prefix):

package main

import (
	"net/http"

	"github.com/go-faster/fs/server"
	"github.com/go-faster/fs/storagefs"
)

func main() {
	store, err := storagefs.New("/data")
	if err != nil {
		panic(err)
	}

	mux := http.NewServeMux()
	mux.Handle("/s3/", http.StripPrefix("/s3", server.NewHandler(store)))

	http.ListenAndServe(":8080", mux)
}
Run the turnkey server

Use server.New for a managed server with a health endpoint, request timeouts, optional bucket pre-creation and graceful shutdown driven by a context:

package main

import (
	"context"
	"os/signal"
	"syscall"

	"github.com/go-faster/fs/server"
	"github.com/go-faster/fs/storagemem"
)

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	srv, err := server.New(server.Config{
		Storage: storagemem.New(),
		Addr:    ":9000",
		Buckets: []string{"uploads"}, // pre-created if absent
	})
	if err != nil {
		panic(err)
	}

	// Serves until ctx is canceled, then drains in-flight requests.
	if err := srv.ListenAndServe(ctx); err != nil {
		panic(err)
	}
}
server.Config
Field Default Description
Storage — (required) Backend serving S3 operations (fs.Storage).
Addr :8080 TCP address to listen on.
ReadTimeout / WriteTimeout / IdleTimeout 30s / 30s / 120s Underlying http.Server timeouts.
HealthPath /health Plaintext health endpoint; "-" disables it.
Buckets Buckets created (idempotently) before serving.
WrapHandler Wrap the handler with middleware/observability (e.g. otelhttp.NewHandler).

See the server package reference for the full API and runnable examples.

Development

# Run tests
go test ./...

# Build
go build ./cmd/fs

# Run with coverage
make coverage

License

Apache 2.0

Documentation

Overview

Package fs is a S3-compatible storage server implementation.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrBucketNotFound       = errors.New("bucket not found")
	ErrBucketAlreadyExists  = errors.New("bucket already exists")
	ErrBucketNotEmpty       = errors.New("bucket not empty")
	ErrObjectNotFound       = errors.New("object not found")
	ErrUploadNotFound       = errors.New("upload not found")
	ErrInvalidBucketName    = errors.New("invalid bucket name")
	ErrUnsupportedOperation = errors.New("unsupported operation")
	ErrPreconditionFailed   = errors.New("precondition failed")

	// ErrInvalidPart reports that a part referenced by CompleteMultipartUpload
	// was never uploaded or its ETag does not match.
	ErrInvalidPart = errors.New("invalid part")
	// ErrInvalidPartOrder reports that the CompleteMultipartUpload part list is
	// not in strictly ascending part-number order.
	ErrInvalidPartOrder = errors.New("invalid part order")
	// ErrInvalidPartNumber reports a part number outside the valid 1..10000 range.
	ErrInvalidPartNumber = errors.New("invalid part number")
	// ErrEntityTooSmall reports a non-last multipart part smaller than the 5 MiB
	// minimum.
	ErrEntityTooSmall = errors.New("entity too small")
	// ErrInvalidTag reports an object tag set violating the S3 limits
	// (at most 10 tags, unique keys, key ≤ 128 chars, value ≤ 256 chars).
	ErrInvalidTag = errors.New("invalid tag")
)

Functions

This section is empty.

Types

type Bucket

type Bucket struct {
	Name         string
	CreationDate time.Time
}

Bucket represents an S3 bucket.

type CompleteMultipartUploadRequest added in v0.0.4

type CompleteMultipartUploadRequest struct {
	Bucket   string
	Key      string
	UploadID string
	Parts    []CompletedPart
}

CompleteMultipartUploadRequest represents a request to complete multipart upload.

type CompleteMultipartUploadResponse added in v0.0.4

type CompleteMultipartUploadResponse struct {
	Location string
	Bucket   string
	Key      string
	ETag     string
}

CompleteMultipartUploadResponse represents the response for completing multipart upload.

type CompletedPart added in v0.0.4

type CompletedPart struct {
	PartNumber int
	ETag       string
}

CompletedPart represents a completed part for completing multipart upload.

type CreateMultipartUploadRequest added in v0.4.0

type CreateMultipartUploadRequest struct {
	Bucket   string
	Key      string
	Metadata ObjectMetadata
	Tags     []Tag
}

CreateMultipartUploadRequest represents a request to start a multipart upload. Metadata and tags are applied to the object at completion.

type GetObjectResponse

type GetObjectResponse struct {
	Reader       io.ReadCloser
	Size         int64
	LastModified time.Time
	ETag         string
	Metadata     ObjectMetadata
}

GetObjectResponse represents the response for GetObject operation.

type MultipartUpload added in v0.0.4

type MultipartUpload struct {
	UploadID  string
	Bucket    string
	Key       string
	Initiated time.Time
}

MultipartUpload represents an in-progress multipart upload.

type Object

type Object struct {
	Key          string
	Size         int64
	LastModified time.Time
	ETag         string
}

Object represents an S3 object.

type ObjectMetadata added in v0.4.0

type ObjectMetadata struct {
	ContentType        string
	CacheControl       string
	ContentDisposition string
	ContentEncoding    string
	// UserMetadata holds x-amz-meta-* pairs, keyed by the lowercase name
	// without the prefix (e.g. "color" for x-amz-meta-color).
	UserMetadata map[string]string
}

ObjectMetadata holds the user-controlled metadata stored with an object: the standard HTTP representation headers plus x-amz-meta-* pairs.

func (ObjectMetadata) IsZero added in v0.4.0

func (m ObjectMetadata) IsZero() bool

IsZero reports whether no metadata field is set.

type Part added in v0.0.4

type Part struct {
	PartNumber   int
	ETag         string
	Size         int64
	LastModified time.Time
}

Part represents a part of a multipart upload.

type PutObjectRequest

type PutObjectRequest struct {
	Reader   io.Reader
	Bucket   string
	Key      string
	Size     int64
	Metadata ObjectMetadata
	Tags     []Tag

	// IfNoneMatch and IfMatch carry the raw conditional-write header values
	// (e.g. "*" or a quoted ETag list). When set, the storage backend must
	// evaluate them atomically with the write — see PreconditionFailed — so
	// concurrent conditional PUTs resolve to a single winner. Empty means no
	// condition.
	IfNoneMatch string
	IfMatch     string
}

func (*PutObjectRequest) PreconditionFailed added in v0.4.0

func (r *PutObjectRequest) PreconditionFailed(exists bool, currentETag string) bool

PreconditionFailed reports whether the request's If-None-Match / If-Match conditions fail against the current object state, where exists reports whether the target object is present and currentETag is its ETag (quoted or bare; only meaningful when exists is true). A true result means the write must be rejected with ErrPreconditionFailed.

Storage backends MUST call this while holding the lock that serializes writes to the key, so the evaluation is atomic with the write. Evaluating the condition in a separate step before the write (check-then-act) races: several concurrent If-None-Match: * writers can all observe "absent" and all succeed.

Semantics (matching S3):

  • If-None-Match: * fail if the object exists.
  • If-None-Match: "<etag>" fail if it exists and the ETag matches.
  • If-Match: * fail if the object does not exist.
  • If-Match: "<etag>" fail if it is missing or the ETag differs.

type PutObjectResponse added in v0.4.0

type PutObjectResponse struct {
	ETag string
}

PutObjectResponse reports the stored object's ETag.

type Storage

type Storage interface {
	ListBuckets(ctx context.Context) ([]Bucket, error)
	CreateBucket(ctx context.Context, bucket string) error
	DeleteBucket(ctx context.Context, bucket string) error
	BucketExists(ctx context.Context, bucket string) (bool, error)
	ListObjects(ctx context.Context, bucket, prefix string) ([]Object, error)
	PutObject(ctx context.Context, req *PutObjectRequest) (*PutObjectResponse, error)
	GetObject(ctx context.Context, bucket, key string) (*GetObjectResponse, error)
	DeleteObject(ctx context.Context, bucket, key string) error

	// GetObjectTagging returns the object's tag set (empty when untagged).
	GetObjectTagging(ctx context.Context, bucket, key string) ([]Tag, error)
	// PutObjectTagging replaces the object's tag set.
	PutObjectTagging(ctx context.Context, bucket, key string, tags []Tag) error
	// DeleteObjectTagging removes the object's tag set.
	DeleteObjectTagging(ctx context.Context, bucket, key string) error

	CreateMultipartUpload(ctx context.Context, req *CreateMultipartUploadRequest) (*MultipartUpload, error)
	UploadPart(ctx context.Context, req *UploadPartRequest) (*Part, error)
	// ListParts returns the parts uploaded so far for an in-progress multipart
	// upload, sorted by ascending part number.
	ListParts(ctx context.Context, bucket, key, uploadID string) ([]Part, error)
	// ListMultipartUploads returns the in-progress multipart uploads for a
	// bucket, sorted by object key (then upload ID for equal keys).
	ListMultipartUploads(ctx context.Context, bucket string) ([]MultipartUpload, error)
	CompleteMultipartUpload(ctx context.Context, req *CompleteMultipartUploadRequest) (*CompleteMultipartUploadResponse, error)
	AbortMultipartUpload(ctx context.Context, bucket, key, uploadID string) error
}

Storage defines the interface for S3-compatible storage operations.

type Tag added in v0.4.0

type Tag struct {
	Key   string
	Value string
}

Tag is a single object tag.

type UploadPartRequest added in v0.0.4

type UploadPartRequest struct {
	Bucket     string
	Key        string
	UploadID   string
	PartNumber int
	Reader     io.Reader
	Size       int64
}

UploadPartRequest represents a request to upload a part.

Directories

Path Synopsis
cmd
fs command
internal
s3err
Package s3err renders S3-compatible error responses.
Package s3err renders S3-compatible error responses.
scripts
gencompat command
Command gencompat generates docs/CONFORMANCE.md from the ceph/s3-tests allow-list, grouping the passing tests by feature area.
Command gencompat generates docs/CONFORMANCE.md from the ceph/s3-tests allow-list, grouping the passing tests by feature area.
Package server provides an embeddable S3-compatible HTTP server.
Package server provides an embeddable S3-compatible HTTP server.
Package storagefs implements fs.Storage.
Package storagefs implements fs.Storage.
Package storagemem implements fs.Storage using in-memory storage.
Package storagemem implements fs.Storage using in-memory storage.
Package storagetest provides a conformance test suite for fs.Storage implementations.
Package storagetest provides a conformance test suite for fs.Storage implementations.

Jump to

Keyboard shortcuts

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