Files
agent-tools/internal/agent/vault.go
T
unkin-agent 8403741902
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
lint: check deferred resp.Body.Close error returns
golangci-lint v2 (errcheck) flagged three unchecked resp.Body.Close()
returns in gitea.go and vault.go. Wrap each deferred Close in a closure
discarding the return, the idiomatic form for a deferred Close whose
error is intentionally ignored.
2026-08-12 21:46:40 +10:00

84 lines
2.5 KiB
Go

package agent
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
// httpClient is shared by the Vault and Gitea calls. A modest timeout keeps a
// hung endpoint from wedging watchpr's poll loop.
var httpClient = &http.Client{Timeout: 30 * time.Second}
// approleLogin logs in with role_id only (no secret_id) and returns the
// resulting client_token.
func approleLogin(vaultAddr, roleID string) (string, error) {
body, _ := json.Marshal(map[string]string{"role_id": roleID})
url := strings.TrimRight(vaultAddr, "/") + "/v1/auth/approle/login"
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("vault approle login: %w", err)
}
defer func() { _ = resp.Body.Close() }()
data, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("vault approle login: HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(data)))
}
var out struct {
Auth struct {
ClientToken string `json:"client_token"`
} `json:"auth"`
}
if err := json.Unmarshal(data, &out); err != nil {
return "", fmt.Errorf("vault approle login: decoding response: %w", err)
}
if out.Auth.ClientToken == "" {
return "", fmt.Errorf("vault approle login: no client_token in response")
}
return out.Auth.ClientToken, nil
}
// readGiteaCreds reads the Gitea creds secret and returns the token field.
func readGiteaCreds(vaultAddr, clientToken string) (string, error) {
url := strings.TrimRight(vaultAddr, "/") + "/v1/" + GiteaCredsPath
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return "", err
}
req.Header.Set("X-Vault-Token", clientToken)
resp, err := httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("vault read %s: %w", GiteaCredsPath, err)
}
defer func() { _ = resp.Body.Close() }()
data, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("vault read %s: HTTP %d: %s", GiteaCredsPath, resp.StatusCode, strings.TrimSpace(string(data)))
}
var out struct {
Data struct {
Token string `json:"token"`
} `json:"data"`
}
if err := json.Unmarshal(data, &out); err != nil {
return "", fmt.Errorf("vault read %s: decoding response: %w", GiteaCredsPath, err)
}
if out.Data.Token == "" {
return "", fmt.Errorf("vault read %s: no token field in secret", GiteaCredsPath)
}
return out.Data.Token, nil
}