Introduced `GetActiveDir` and `CleanActiveDir` methods in the blob store to manage a dedicated directory for active snapshot operations. This ensures a clean working state before starting new operations and prevents conflicts. Updated related logic in snapshot creation and restoration to utilize the active directory.
310 lines
8.1 KiB
Markdown
310 lines
8.1 KiB
Markdown
# Agate
|
|
|
|
Agate is a Go library for creating, managing, and sharing snapshots of directories. It provides functionality for creating incremental snapshots, storing them efficiently, and sharing them over a network using gRPC.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
go get gitea.unprism.ru/KRBL/Agate
|
|
```
|
|
|
|
## Features
|
|
|
|
- Create snapshots of directories
|
|
- Incremental snapshots (only store changes)
|
|
- Restore snapshots
|
|
- List and manage snapshots
|
|
- Share snapshots over a network using gRPC
|
|
- Connect to remote snapshot repositories
|
|
|
|
## Basic Usage
|
|
|
|
### Creating a Snapshot Repository
|
|
|
|
To create a snapshot repository, you need to initialize the Agate library with the appropriate options:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gitea.unprism.ru/KRBL/Agate"
|
|
"gitea.unprism.ru/KRBL/Agate/stores"
|
|
)
|
|
|
|
func main() {
|
|
// Create directories for your repository
|
|
workDir := "/path/to/your/repository"
|
|
if err := os.MkdirAll(workDir, 0755); err != nil {
|
|
log.Fatalf("Failed to create work directory: %v", err)
|
|
}
|
|
|
|
// Initialize the default stores
|
|
metadataStore, blobStore, err := stores.InitDefaultStores(workDir)
|
|
if err != nil {
|
|
log.Fatalf("Failed to initialize stores: %v", err)
|
|
}
|
|
defer metadataStore.Close()
|
|
|
|
// Initialize Agate
|
|
agateOptions := agate.AgateOptions{
|
|
WorkDir: workDir,
|
|
MetadataStore: metadataStore,
|
|
BlobStore: blobStore,
|
|
}
|
|
|
|
ag, err := agate.New(agateOptions)
|
|
if err != nil {
|
|
log.Fatalf("Failed to initialize Agate: %v", err)
|
|
}
|
|
defer ag.Close()
|
|
|
|
// Create a snapshot
|
|
ctx := context.Background()
|
|
snapshotID, err := ag.SaveSnapshot(ctx, "My First Snapshot", "")
|
|
if err != nil {
|
|
log.Fatalf("Failed to create snapshot: %v", err)
|
|
}
|
|
fmt.Printf("Created snapshot with ID: %s\n", snapshotID)
|
|
|
|
// List snapshots
|
|
snapshots, err := ag.ListSnapshots(ctx)
|
|
if err != nil {
|
|
log.Fatalf("Failed to list snapshots: %v", err)
|
|
}
|
|
fmt.Printf("Found %d snapshots:\n", len(snapshots))
|
|
for _, s := range snapshots {
|
|
fmt.Printf(" - %s: %s (created at %s)\n", s.ID, s.Name, s.CreationTime.Format("2006-01-02 15:04:05"))
|
|
}
|
|
}
|
|
```
|
|
|
|
### Hosting a Snapshot Repository
|
|
|
|
To host a snapshot repository and make it available over the network, you can use the `StartServer` method:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"gitea.unprism.ru/KRBL/Agate"
|
|
"gitea.unprism.ru/KRBL/Agate/stores"
|
|
)
|
|
|
|
func main() {
|
|
// Create directories for your repository
|
|
workDir := "/path/to/your/repository"
|
|
if err := os.MkdirAll(workDir, 0755); err != nil {
|
|
log.Fatalf("Failed to create work directory: %v", err)
|
|
}
|
|
|
|
// Initialize the default stores
|
|
metadataStore, blobStore, err := stores.InitDefaultStores(workDir)
|
|
if err != nil {
|
|
log.Fatalf("Failed to initialize stores: %v", err)
|
|
}
|
|
defer metadataStore.Close()
|
|
|
|
// Initialize Agate
|
|
agateOptions := agate.AgateOptions{
|
|
WorkDir: workDir,
|
|
MetadataStore: metadataStore,
|
|
BlobStore: blobStore,
|
|
}
|
|
|
|
ag, err := agate.New(agateOptions)
|
|
if err != nil {
|
|
log.Fatalf("Failed to initialize Agate: %v", err)
|
|
}
|
|
defer ag.Close()
|
|
|
|
// Start the gRPC server
|
|
ctx := context.Background()
|
|
address := "0.0.0.0:50051" // Listen on all interfaces, port 50051
|
|
if err := ag.StartServer(ctx, address); err != nil {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
|
|
log.Printf("Server started on %s", address)
|
|
|
|
// Wait for termination signal
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
|
<-sigCh
|
|
|
|
log.Println("Shutting down...")
|
|
}
|
|
```
|
|
|
|
### Connecting to a Hosted Snapshot Repository
|
|
|
|
To connect to a hosted snapshot repository and retrieve snapshots:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"gitea.unprism.ru/KRBL/Agate"
|
|
"gitea.unprism.ru/KRBL/Agate/stores"
|
|
)
|
|
|
|
func main() {
|
|
// Create directories for your local repository
|
|
workDir := "/path/to/your/local/repository"
|
|
if err := os.MkdirAll(workDir, 0755); err != nil {
|
|
log.Fatalf("Failed to create work directory: %v", err)
|
|
}
|
|
|
|
// Initialize the default stores
|
|
metadataStore, blobStore, err := stores.InitDefaultStores(workDir)
|
|
if err != nil {
|
|
log.Fatalf("Failed to initialize stores: %v", err)
|
|
}
|
|
defer metadataStore.Close()
|
|
|
|
// Initialize Agate
|
|
agateOptions := agate.AgateOptions{
|
|
WorkDir: workDir,
|
|
MetadataStore: metadataStore,
|
|
BlobStore: blobStore,
|
|
}
|
|
|
|
ag, err := agate.New(agateOptions)
|
|
if err != nil {
|
|
log.Fatalf("Failed to initialize Agate: %v", err)
|
|
}
|
|
defer ag.Close()
|
|
|
|
// Connect to a remote server
|
|
ctx := context.Background()
|
|
remoteAddress := "remote-server:50051"
|
|
|
|
// List snapshots from the remote server
|
|
snapshots, err := ag.GetRemoteSnapshotList(ctx, remoteAddress)
|
|
if err != nil {
|
|
log.Fatalf("Failed to list remote snapshots: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Found %d remote snapshots:\n", len(snapshots))
|
|
for _, s := range snapshots {
|
|
fmt.Printf(" - %s: %s (created at %s)\n", s.ID, s.Name, s.CreationTime.Format("2006-01-02 15:04:05"))
|
|
}
|
|
|
|
// Download a specific snapshot
|
|
if len(snapshots) > 0 {
|
|
snapshotID := snapshots[0].ID
|
|
fmt.Printf("Downloading snapshot %s...\n", snapshotID)
|
|
|
|
// Download the snapshot (pass empty string as localParentID if this is the first download)
|
|
if err := ag.GetRemoteSnapshot(ctx, remoteAddress, snapshotID, ""); err != nil {
|
|
log.Fatalf("Failed to download snapshot: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Successfully downloaded snapshot %s\n", snapshotID)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Advanced Usage
|
|
|
|
### Creating Incremental Snapshots
|
|
|
|
You can create incremental snapshots by specifying a parent snapshot ID:
|
|
|
|
```go
|
|
// Create a first snapshot
|
|
snapshotID1, err := ag.SaveSnapshot(ctx, "First Snapshot", "")
|
|
if err != nil {
|
|
log.Fatalf("Failed to create first snapshot: %v", err)
|
|
}
|
|
|
|
// Make some changes to your files...
|
|
|
|
// Create a second snapshot with the first one as parent
|
|
snapshotID2, err := ag.SaveSnapshot(ctx, "Second Snapshot", snapshotID1)
|
|
if err != nil {
|
|
log.Fatalf("Failed to create second snapshot: %v", err)
|
|
}
|
|
```
|
|
|
|
### Restoring a Snapshot
|
|
|
|
To restore a snapshot:
|
|
|
|
```go
|
|
if err := ag.RestoreSnapshot(ctx, snapshotID); err != nil {
|
|
log.Fatalf("Failed to restore snapshot: %v", err)
|
|
}
|
|
```
|
|
|
|
### Getting Snapshot Details
|
|
|
|
To get detailed information about a snapshot:
|
|
|
|
```go
|
|
snapshot, err := ag.GetSnapshotDetails(ctx, snapshotID)
|
|
if err != nil {
|
|
log.Fatalf("Failed to get snapshot details: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Snapshot: %s\n", snapshot.Name)
|
|
fmt.Printf("Created: %s\n", snapshot.CreationTime.Format("2006-01-02 15:04:05"))
|
|
fmt.Printf("Files: %d\n", len(snapshot.Files))
|
|
```
|
|
|
|
### Deleting a Snapshot
|
|
|
|
To delete a snapshot:
|
|
|
|
```go
|
|
if err := ag.DeleteSnapshot(ctx, snapshotID); err != nil {
|
|
log.Fatalf("Failed to delete snapshot: %v", err)
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### Agate
|
|
|
|
The main entry point for the library.
|
|
|
|
- `New(options AgateOptions) (*Agate, error)` - Create a new Agate instance
|
|
- `SaveSnapshot(ctx context.Context, name string, parentID string) (string, error)` - Create a new snapshot
|
|
- `RestoreSnapshot(ctx context.Context, snapshotID string) error` - Restore a snapshot
|
|
- `ListSnapshots(ctx context.Context) ([]store.SnapshotInfo, error)` - List all snapshots
|
|
- `GetSnapshotDetails(ctx context.Context, snapshotID string) (*store.Snapshot, error)` - Get details of a snapshot
|
|
- `DeleteSnapshot(ctx context.Context, snapshotID string) error` - Delete a snapshot
|
|
- `StartServer(ctx context.Context, address string) error` - Start a gRPC server to share snapshots
|
|
- `ConnectRemote(address string) (*grpc.SnapshotClient, error)` - Connect to a remote server
|
|
- `GetRemoteSnapshotList(ctx context.Context, address string) ([]store.SnapshotInfo, error)` - List snapshots from a remote server
|
|
- `GetRemoteSnapshot(ctx context.Context, address string, snapshotID string, localParentID string) error` - Download a snapshot from a remote server
|
|
|
|
### AgateOptions
|
|
|
|
Configuration options for the Agate library.
|
|
|
|
- `WorkDir string` - Directory where snapshots will be stored and managed
|
|
- `OpenFunc func(dir string) error` - Called after a snapshot is restored
|
|
- `CloseFunc func() error` - Called before a snapshot is created or restored
|
|
- `MetadataStore store.MetadataStore` - Implementation of the metadata store
|
|
- `BlobStore store.BlobStore` - Implementation of the blob store
|
|
|
|
## License
|
|
|
|
[Add your license information here] |