Agate/api.go
Alexander Lazarenko 7e9cea9227
Add initial implementation of Agate snapshot library
Introduces core functionality for the Agate library, including snapshot creation, restoration, listing, and deletion. Adds examples for basic usage, gRPC proto definitions, and build/configuration files such as `go.mod` and `Makefile`. The implementation establishes the framework for store integration and placeholder server functionality.
2025-04-24 02:44:16 +03:00

178 lines
5.6 KiB
Go

package agate
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"unprism.ru/KRBL/agate/store"
)
// AgateOptions defines configuration options for the Agate library.
type AgateOptions struct {
// WorkDir is the directory where snapshots will be stored and managed.
WorkDir string
// OpenFunc is called after a snapshot is restored to initialize resources.
// The parameter is the directory where the snapshot was extracted.
OpenFunc func(dir string) error
// CloseFunc is called before a snapshot is created or restored to clean up resources.
CloseFunc func() error
// MetadataStore is the implementation of the metadata store to use.
// Use the stores package to initialize the default implementation:
// metadataStore, err := stores.NewDefaultMetadataStore(metadataDir)
MetadataStore store.MetadataStore
// BlobStore is the implementation of the blob store to use.
// Use the stores package to initialize the default implementation:
// blobStore, err := stores.NewDefaultBlobStore(blobsDir)
BlobStore store.BlobStore
}
// Agate is the main entry point for the snapshot library.
type Agate struct {
manager SnapshotManager
options AgateOptions
metadataDir string
blobsDir string
}
// New initializes a new instance of the Agate library with the given options.
func New(options AgateOptions) (*Agate, error) {
if options.WorkDir == "" {
return nil, errors.New("work directory cannot be empty")
}
// Create the work directory if it doesn't exist
if err := os.MkdirAll(options.WorkDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create work directory: %w", err)
}
// Create subdirectories for metadata and blobs
metadataDir := filepath.Join(options.WorkDir, "metadata")
blobsDir := filepath.Join(options.WorkDir, "blobs")
if err := os.MkdirAll(metadataDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create metadata directory: %w", err)
}
if err := os.MkdirAll(blobsDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create blobs directory: %w", err)
}
var metadataStore store.MetadataStore
var blobStore store.BlobStore
var err error
// Use provided stores or initialize default ones
if options.MetadataStore != nil {
metadataStore = options.MetadataStore
} else {
// For default implementation, the user needs to initialize and provide the stores
return nil, errors.New("metadata store must be provided")
}
if options.BlobStore != nil {
blobStore = options.BlobStore
} else {
// For default implementation, the user needs to initialize and provide the stores
return nil, errors.New("blob store must be provided")
}
// Create the snapshot manager
manager, err := CreateSnapshotManager(metadataStore, blobStore)
if err != nil {
return nil, fmt.Errorf("failed to create snapshot manager: %w", err)
}
return &Agate{
manager: manager,
options: options,
metadataDir: metadataDir,
blobsDir: blobsDir,
}, nil
}
// SaveSnapshot creates a new snapshot from the current state of the work directory.
// If parentID is provided, it will be set as the parent of the new snapshot.
// Returns the ID of the created snapshot.
func (a *Agate) SaveSnapshot(ctx context.Context, name string, parentID string) (string, error) {
// Call CloseFunc if provided
if a.options.CloseFunc != nil {
if err := a.options.CloseFunc(); err != nil {
return "", fmt.Errorf("failed to close resources before snapshot: %w", err)
}
}
// Create the snapshot
snapshot, err := a.manager.CreateSnapshot(ctx, a.options.WorkDir, name, parentID)
if err != nil {
return "", fmt.Errorf("failed to create snapshot: %w", err)
}
// Call OpenFunc if provided
if a.options.OpenFunc != nil {
if err := a.options.OpenFunc(a.options.WorkDir); err != nil {
return "", fmt.Errorf("failed to open resources after snapshot: %w", err)
}
}
return snapshot.ID, nil
}
// RestoreSnapshot extracts a snapshot to the work directory.
func (a *Agate) RestoreSnapshot(ctx context.Context, snapshotID string) error {
// Call CloseFunc if provided
if a.options.CloseFunc != nil {
if err := a.options.CloseFunc(); err != nil {
return fmt.Errorf("failed to close resources before restore: %w", err)
}
}
// Extract the snapshot
if err := a.manager.ExtractSnapshot(ctx, snapshotID, a.options.WorkDir); err != nil {
return fmt.Errorf("failed to extract snapshot: %w", err)
}
// Call OpenFunc if provided
if a.options.OpenFunc != nil {
if err := a.options.OpenFunc(a.options.WorkDir); err != nil {
return fmt.Errorf("failed to open resources after restore: %w", err)
}
}
return nil
}
// ListSnapshots returns a list of all available snapshots.
func (a *Agate) ListSnapshots(ctx context.Context) ([]store.SnapshotInfo, error) {
return a.manager.ListSnapshots(ctx)
}
// GetSnapshotDetails returns detailed information about a specific snapshot.
func (a *Agate) GetSnapshotDetails(ctx context.Context, snapshotID string) (*store.Snapshot, error) {
return a.manager.GetSnapshotDetails(ctx, snapshotID)
}
// DeleteSnapshot removes a snapshot.
func (a *Agate) DeleteSnapshot(ctx context.Context, snapshotID string) error {
return a.manager.DeleteSnapshot(ctx, snapshotID)
}
// Close releases all resources used by the Agate instance.
func (a *Agate) Close() error {
// Currently, we don't have a way to close the manager directly
// This would be a good addition in the future
return nil
}
// StartServer starts a gRPC server to share snapshots.
// This is a placeholder for future implementation.
func (a *Agate) StartServer(ctx context.Context, address string) error {
return errors.New("server functionality not implemented yet")
}