Introduce methods to register local snapshots from archives and to download or restore snapshot metadata, improving snapshot management capabilities. Update README with usage examples.
344 lines
9.5 KiB
Markdown
344 lines
9.5 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
|
|
|
|
### Registering a Local Snapshot
|
|
|
|
You can register a local snapshot from an existing archive file with a specified UUID:
|
|
|
|
```go
|
|
// Register a local snapshot from an archive file
|
|
archivePath := "/path/to/your/archive.zip"
|
|
snapshotID := "custom-uuid-for-snapshot"
|
|
snapshotName := "My Local Snapshot"
|
|
|
|
if err := ag.RegisterLocalSnapshot(ctx, archivePath, snapshotID, snapshotName); err != nil {
|
|
log.Fatalf("Failed to register local snapshot: %v", err)
|
|
}
|
|
```
|
|
|
|
### Downloading Only Snapshot Metadata
|
|
|
|
You can download only the metadata of a snapshot from a remote server without downloading the actual files:
|
|
|
|
```go
|
|
// Download only the metadata of a snapshot from a remote server
|
|
remoteAddress := "remote-server:50051"
|
|
snapshotID := "snapshot-id-to-download"
|
|
|
|
if err := ag.GetRemoteSnapshotMetadata(ctx, remoteAddress, snapshotID); err != nil {
|
|
log.Fatalf("Failed to download snapshot metadata: %v", err)
|
|
}
|
|
|
|
// If you have a local blob but missing metadata, you can restore the metadata
|
|
// by passing an empty address
|
|
if err := ag.GetRemoteSnapshotMetadata(ctx, "", snapshotID); err != nil {
|
|
log.Fatalf("Failed to restore snapshot metadata: %v", err)
|
|
}
|
|
```
|
|
|
|
### 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
|
|
- `RegisterLocalSnapshot(ctx context.Context, archivePath string, snapshotID string, name string) error` - Register a local snapshot from an archive path with a specified UUID
|
|
- `GetRemoteSnapshotMetadata(ctx context.Context, address string, snapshotID string) error` - Download only the metadata of 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
|