Files
infra-kit/examples/go-service/main.go
T
stolzor 64be99308b
terraform / check (push) Canceled after 0s
Initial commit
2026-10-11 20:30:21 +03:00

135 lines
4.1 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
vault "github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/api/auth/approle"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
func main() {
ctx := context.Background()
service := mustEnv("SERVICE_NAME")
kvMount := mustEnv("VAULT_KV_MOUNT")
client, err := vault.NewClient(vault.DefaultConfig())
check(err, "клиент Vault")
auth, err := approle.NewAppRoleAuth(mustEnv("VAULT_ROLE_ID"), &approle.SecretID{FromEnv: "VAULT_SECRET_ID"})
check(err, "AppRole")
login, err := client.Auth().Login(ctx, auth)
check(err, "вход в Vault")
log.Printf("vault: вошёл, политики %v, токен на %dс", login.Auth.Policies, login.Auth.LeaseDuration)
kv := client.KVv2(kvMount)
s3Secret, err := kv.Get(ctx, service+"/s3")
check(err, "секрет s3")
pgSecret, err := kv.Get(ctx, service+"/postgres")
check(err, "секрет postgres")
pgUser, pgPassword := str(pgSecret.Data, "username"), str(pgSecret.Data, "password")
if os.Getenv("POSTGRES_CREDS") == "dynamic" {
creds, err := client.Logical().ReadWithContext(ctx, mustEnv("VAULT_DB_MOUNT")+"/creds/"+service)
check(err, "временные учётные данные")
pgUser, pgPassword = str(creds.Data, "username"), str(creds.Data, "password")
log.Printf("postgres: временная роль %s на %dс", pgUser, creds.LeaseDuration)
}
dsn := url.URL{
Scheme: "postgres",
User: url.UserPassword(pgUser, pgPassword),
Host: fmt.Sprintf("%s:%s", str(pgSecret.Data, "host"), str(pgSecret.Data, "port")),
Path: str(pgSecret.Data, "database"),
RawQuery: "sslmode=" + str(pgSecret.Data, "sslmode"),
}
db, err := pgxpool.New(ctx, dsn.String())
check(err, "пул postgres")
defer db.Close()
check(db.Ping(ctx), "подключение к postgres")
log.Printf("postgres: подключился к %s как %s", dsn.Path, pgUser)
endpoint, err := url.Parse(str(s3Secret.Data, "endpoint"))
check(err, "endpoint s3")
s3, err := minio.New(endpoint.Host, &minio.Options{
Creds: credentials.NewStaticV4(str(s3Secret.Data, "access_key"), str(s3Secret.Data, "secret_key"), ""),
Secure: endpoint.Scheme == "https",
})
check(err, "клиент s3")
buckets := strings.Split(str(s3Secret.Data, "buckets"), ",")
log.Printf("s3: бакеты %v на %s", buckets, endpoint.Host)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
defer cancel()
status := map[string]any{"service": service}
var sessionUser, currentUser, database string
err := db.QueryRow(ctx, "select session_user, current_user, current_database()").
Scan(&sessionUser, &currentUser, &database)
if err != nil {
status["postgres"] = map[string]string{"error": err.Error()}
} else {
status["postgres"] = map[string]string{
"database": database, "session_user": sessionUser, "current_user": currentUser,
}
}
objects := map[string]any{}
for _, bucket := range buckets {
count := 0
var listErr error
for object := range s3.ListObjects(ctx, bucket, minio.ListObjectsOptions{Recursive: true}) {
if object.Err != nil {
listErr = object.Err
break
}
count++
}
if listErr != nil {
objects[bucket] = map[string]string{"error": listErr.Error()}
} else {
objects[bucket] = map[string]int{"objects": count}
}
}
status["s3"] = objects
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(status)
})
log.Print("http: слушаю :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func mustEnv(name string) string {
value := os.Getenv(name)
if value == "" {
log.Fatalf("не задана переменная окружения %s", name)
}
return value
}
func check(err error, what string) {
if err != nil {
log.Fatalf("%s: %v", what, err)
}
}
func str(data map[string]any, key string) string {
value, ok := data[key]
if !ok {
log.Fatalf("в секрете нет поля %s", key)
}
return fmt.Sprint(value)
}