Files

259 lines
6.5 KiB
Go

package minio
import (
"context"
"net/http"
"time"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
// ConnectionManager manages MinIO connections and operations
type ConnectionManager struct {
client *minio.Client
config Config
logger Logger
}
// NewConnectionManager creates a new MinIO connection manager
func NewConnectionManager(config Config, logger Logger) (*ConnectionManager, error) {
if err := config.Validate(); err != nil {
return nil, err
}
return &ConnectionManager{
config: config,
logger: logger,
}, nil
}
// Connect establishes connection to MinIO
func (cm *ConnectionManager) Connect() error {
// Create MinIO client
minioClient, err := minio.New(cm.config.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(cm.config.AccessKeyID, cm.config.SecretAccessKey, ""),
Secure: cm.config.UseSSL,
Region: cm.config.Region,
Transport: &http.Transport{
ResponseHeaderTimeout: cm.config.RequestTimeout,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: false,
},
})
if err != nil {
cm.logger.Error("Failed to create MinIO client", map[string]interface{}{
"endpoint": cm.config.Endpoint,
"error": err.Error(),
})
return NewErrorWithCause(ErrCodeConnectionFailed, "failed to create MinIO client", err)
}
cm.client = minioClient
// Test connection by checking if we can reach the server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Try bucket exists check with shorter timeout
exists, err := cm.client.BucketExists(ctx, cm.config.DefaultBucket)
if err != nil {
cm.logger.Error("Failed to connect to MinIO", map[string]interface{}{
"endpoint": cm.config.Endpoint,
"bucket": cm.config.DefaultBucket,
"error": err.Error(),
})
return NewErrorWithCause(ErrCodeConnectionFailed, "failed to connect to MinIO", err)
}
cm.logger.Info("Successfully connected to MinIO", map[string]interface{}{
"endpoint": cm.config.Endpoint,
"region": cm.config.Region,
"bucket": cm.config.DefaultBucket,
"exists": exists,
})
return nil
}
// Disconnect closes the MinIO connection
func (cm *ConnectionManager) Disconnect(ctx context.Context) error {
if cm.client == nil {
return nil
}
// MinIO client doesn't have a disconnect method, but we can set it to nil
cm.client = nil
cm.logger.Info("Disconnected from MinIO", nil)
return nil
}
// Ping tests the MinIO connection
func (cm *ConnectionManager) Ping(ctx context.Context) error {
if cm.client == nil {
return ErrNotConnected
}
_, err := cm.client.ListBuckets(ctx)
if err != nil {
cm.logger.Error("MinIO ping failed", map[string]interface{}{
"error": err.Error(),
})
return NewErrorWithCause(ErrCodeConnectionFailed, "ping failed", err)
}
return nil
}
// IsConnected returns true if connected to MinIO
func (cm *ConnectionManager) IsConnected() bool {
return cm.client != nil
}
// GetClient returns the underlying MinIO client
func (cm *ConnectionManager) GetClient() interface{} {
return cm.client
}
// CreateBucket creates a new bucket if it doesn't exist
func (cm *ConnectionManager) CreateBucket(ctx context.Context, bucketName string) error {
if cm.client == nil {
return ErrNotConnected
}
if bucketName == "" {
return ErrInvalidBucketName
}
exists, err := cm.client.BucketExists(ctx, bucketName)
if err != nil {
cm.logger.Error("Failed to check bucket existence", map[string]interface{}{
"bucket": bucketName,
"error": err.Error(),
})
return NewErrorWithCause(ErrCodeBucketNotFound, "failed to check bucket existence", err)
}
if exists {
cm.logger.Debug("Bucket already exists", map[string]interface{}{
"bucket": bucketName,
})
return nil
}
err = cm.client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{
Region: cm.config.Region,
})
if err != nil {
cm.logger.Error("Failed to create bucket", map[string]interface{}{
"bucket": bucketName,
"error": err.Error(),
})
return NewErrorWithCause("BUCKET_CREATE_FAILED", "failed to create bucket", err)
}
cm.logger.Info("Bucket created successfully", map[string]interface{}{
"bucket": bucketName,
})
return nil
}
// DeleteBucket deletes a bucket
func (cm *ConnectionManager) DeleteBucket(ctx context.Context, bucketName string) error {
if cm.client == nil {
return ErrNotConnected
}
if bucketName == "" {
return ErrInvalidBucketName
}
err := cm.client.RemoveBucket(ctx, bucketName)
if err != nil {
cm.logger.Error("Failed to delete bucket", map[string]interface{}{
"bucket": bucketName,
"error": err.Error(),
})
return NewErrorWithCause("BUCKET_DELETE_FAILED", "failed to delete bucket", err)
}
cm.logger.Info("Bucket deleted successfully", map[string]interface{}{
"bucket": bucketName,
})
return nil
}
// BucketExists checks if a bucket exists
func (cm *ConnectionManager) BucketExists(ctx context.Context, bucketName string) (bool, error) {
if cm.client == nil {
return false, ErrNotConnected
}
if bucketName == "" {
return false, ErrInvalidBucketName
}
exists, err := cm.client.BucketExists(ctx, bucketName)
if err != nil {
cm.logger.Error("Failed to check bucket existence", map[string]interface{}{
"bucket": bucketName,
"error": err.Error(),
})
return false, NewErrorWithCause(ErrCodeBucketNotFound, "failed to check bucket existence", err)
}
return exists, nil
}
// ListBuckets lists all buckets
func (cm *ConnectionManager) ListBuckets(ctx context.Context) ([]string, error) {
if cm.client == nil {
return nil, ErrNotConnected
}
buckets, err := cm.client.ListBuckets(ctx)
if err != nil {
cm.logger.Error("Failed to list buckets", map[string]interface{}{
"error": err.Error(),
})
return nil, NewErrorWithCause("LIST_BUCKETS_FAILED", "failed to list buckets", err)
}
bucketNames := make([]string, len(buckets))
for i, bucket := range buckets {
bucketNames[i] = bucket.Name
}
cm.logger.Debug("Buckets listed successfully", map[string]interface{}{
"count": len(bucketNames),
})
return bucketNames, nil
}
// GetBucketInfo gets bucket information
func (cm *ConnectionManager) GetBucketInfo(ctx context.Context, bucketName string) (map[string]interface{}, error) {
if cm.client == nil {
return nil, ErrNotConnected
}
if bucketName == "" {
return nil, ErrInvalidBucketName
}
// For now, return basic bucket info
// In a real implementation, you might want to get more detailed info
info := map[string]interface{}{
"name": bucketName,
"region": cm.config.Region,
}
cm.logger.Debug("Bucket info retrieved", map[string]interface{}{
"bucket": bucketName,
})
return info, nil
}