commit
64be99308b
53 files changed
+1983
No files matched your search
@@ -0,0 +1,134 @@
|
||||
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, ¤tUser, &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)
|
||||
}
|
||||
Reference in new issue
Block a user