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") }