Files
Agate/README.md
Alexander Lazarenko efa2bec38b Add RegisterLocalSnapshot and GetRemoteSnapshotMetadata methods
Introduce methods to register local snapshots from archives and to download or restore snapshot metadata, improving snapshot management capabilities. Update README with usage examples.
2025-07-10 12:49:05 +03:00

9.5 KiB

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

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:

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:

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:

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:

// 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:

// 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:

// 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:

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:

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:

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