Files
storage-appliance/internal/infra/minio/minio.go

123 lines
3.9 KiB
Go

package minio
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/example/storage-appliance/internal/infra/osexec"
)
type Adapter struct {
Runner osexec.Runner
EnvPath string
}
func NewAdapter(runner osexec.Runner, envPath string) *Adapter {
if envPath == "" {
envPath = "/etc/minio/minio.env"
}
return &Adapter{Runner: runner, EnvPath: envPath}
}
type Settings struct {
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
DataPath string `json:"data_path"`
Port int `json:"port"`
TLS bool `json:"tls"`
}
// WriteEnv writes environment file used by MinIO service
func (a *Adapter) WriteEnv(ctx context.Context, s Settings) error {
dir := filepath.Dir(a.EnvPath)
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
// env lines
lines := []string{
fmt.Sprintf("MINIO_ROOT_USER=%s", s.AccessKey),
fmt.Sprintf("MINIO_ROOT_PASSWORD=%s", s.SecretKey),
fmt.Sprintf("MINIO_VOLUMES=%s", s.DataPath),
}
if s.Port != 0 {
lines = append(lines, fmt.Sprintf("MINIO_OPTS=--address :%d", s.Port))
}
content := strings.Join(lines, "\n") + "\n"
tmp := filepath.Join(dir, ".minio.env.tmp")
if err := os.WriteFile(tmp, []byte(content), 0600); err != nil {
return err
}
if err := os.Rename(tmp, a.EnvPath); err != nil {
return err
}
return nil
}
// Reload reloads minio service to pick up new env; prefer systemctl reload
func (a *Adapter) Reload(ctx context.Context) error {
_, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "reload", "minio")
if err == nil {
return nil
}
// fallback to restart
_, stderr, _, err = osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "restart", "minio")
if err != nil {
return fmt.Errorf("minio reload/restart failed: %s", stderr)
}
return nil
}
// ConfigureMC configures mc alias to point to the MinIO service using given settings
func (a *Adapter) ConfigureMC(ctx context.Context, alias string, settings Settings) error {
secure := "--insecure"
if settings.TLS {
secure = ""
}
// mc alias set <alias> <endpoint> <access> <secret> [--api S3v4]
endpoint := fmt.Sprintf("http://127.0.0.1:%d", settings.Port)
if settings.TLS {
endpoint = fmt.Sprintf("https://127.0.0.1:%d", settings.Port)
}
_, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "mc", "alias", "set", alias, endpoint, settings.AccessKey, settings.SecretKey, secure)
if err != nil {
return fmt.Errorf("mc alias set failed: %s", stderr)
}
return nil
}
// ListBuckets uses mc to list buckets via alias
func (a *Adapter) ListBuckets(ctx context.Context, alias string) ([]string, error) {
out, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "mc", "ls", "--json", alias)
if err != nil {
return nil, fmt.Errorf("mc ls failed: %s", stderr)
}
// parse JSON lines, each contains a 'key' or 'name' - in mc, `ls --json` returns 'key'
var buckets []string
lines := strings.Split(strings.TrimSpace(out), "\n")
for _, l := range lines {
var obj map[string]any
if err := json.Unmarshal([]byte(l), &obj); err != nil {
continue
}
if otype, ok := obj["type"].(string); ok && otype == "bucket" {
if name, ok := obj["key"].(string); ok {
buckets = append(buckets, name)
}
}
}
return buckets, nil
}
// CreateBucket uses mc to create a new bucket alias/<name>
func (a *Adapter) CreateBucket(ctx context.Context, alias, name string) error {
_, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "mc", "mb", alias+"/"+name)
if err != nil {
return fmt.Errorf("mc mb failed: %s", stderr)
}
return nil
}