package artifactstore import ( "bytes" "context" "fmt" "io" "log/slog" "math" "github.com/hohn/mrvacommander/pkg/common" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" ) type MinIOArtifactStore struct { client *minio.Client } func NewMinIOArtifactStore(endpoint, id, secret string, lookup minio.BucketLookupType) (*MinIOArtifactStore, error) { minioClient, err := minio.New(endpoint, &minio.Options{ Creds: credentials.NewStaticV4(id, secret, ""), Secure: false, BucketLookup: lookup, }) if err != nil { return nil, err } slog.Info("Connected to MinIO artifact store server") return &MinIOArtifactStore{ client: minioClient, }, nil } func (store *MinIOArtifactStore) GetQueryPack(location ArtifactLocation) ([]byte, error) { return store.getArtifact(location) } func (store *MinIOArtifactStore) SaveQueryPack(jobId int, data []byte) (ArtifactLocation, error) { key := fmt.Sprintf("%d", jobId) return store.saveArtifact(AF_BUCKETNAME_PACKS, key, data, "application/gzip") } func (store *MinIOArtifactStore) GetResult(location ArtifactLocation) ([]byte, error) { return store.getArtifact(location) } func (store *MinIOArtifactStore) GetResultSize(location ArtifactLocation) (int, error) { bucket := location.Bucket key := location.Key objectInfo, err := store.client.StatObject(context.Background(), bucket, key, minio.StatObjectOptions{}) if err != nil { return 0, err } if objectInfo.Size > math.MaxInt32 { return 0, fmt.Errorf("object size %d exceeds max int size", objectInfo.Size) } return int(objectInfo.Size), nil } func (store *MinIOArtifactStore) SaveResult(jobSpec common.JobSpec, data []byte) (ArtifactLocation, error) { key := fmt.Sprintf("%d-%s", jobSpec.SessionID, jobSpec.NameWithOwner) return store.saveArtifact(AF_BUCKETNAME_RESULTS, key, data, "application/zip") } func (store *MinIOArtifactStore) getArtifact(location ArtifactLocation) ([]byte, error) { bucket := location.Bucket key := location.Key object, err := store.client.GetObject(context.Background(), bucket, key, minio.GetObjectOptions{}) if err != nil { return nil, err } defer object.Close() data, err := io.ReadAll(object) if err != nil { return nil, err } return data, nil } func (store *MinIOArtifactStore) saveArtifact(bucket, key string, data []byte, contentType string) (ArtifactLocation, error) { exists, err := store.client.BucketExists(context.Background(), bucket) if err != nil || !exists { slog.Error("Bucket does not exist", "bucket", bucket) } _, err = store.client.PutObject(context.Background(), bucket, key, bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{ ContentType: contentType, }) if err != nil { return ArtifactLocation{}, err } location := ArtifactLocation{ Bucket: bucket, Key: key, } return location, nil }