Files
certmanager/internal/config/config_test.go
T
unkin-agent b9633481a5
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Make certmanager a drop-in for the Python script
Puppet calls /usr/local/bin/certmanager from generate() with combine: true, so
the CLI must take the common name positionally and keep stderr clean or the
catalog compile dies on a parsejson error.

- Flatten the CLI: drop the pki subcommand, add -a/-i/-e/-c/--json
- Emit ca_certificate so vault.pp stops writing undef to that file
- Read output_path from under vault:, where Puppet writes it
- Bound every Vault request with a timeout; exit non-zero on failure
- Gate TLS verification on vault.tls_skip_verify (default true)
- Restrict auth to approle and kubernetes, no fallback between them
- Remove the SSH half; add Woodpecker CI, nfpm RPM and completions
2026-09-19 10:34:01 +10:00

128 lines
4.3 KiB
Go

package config
import (
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func writeConfig(t *testing.T, body string) string {
t.Helper()
path := filepath.Join(t.TempDir(), "config.yaml")
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatalf("write config: %v", err)
}
return path
}
const approleBody = `vault:
addr: https://vault.service.consul:8200
approle_path: approle
role_id: abc-123
mount_point: pki_int
role_name: servers_default
output_path: /tmp/certmanager
`
func TestLoadAppRoleDefaults(t *testing.T) {
cfg, err := Load(writeConfig(t, approleBody))
if err != nil {
t.Fatalf("Load: %v", err)
}
if cfg.Vault.AuthMethod != AuthMethodAppRole {
t.Errorf("auth_method = %q, want %q (default)", cfg.Vault.AuthMethod, AuthMethodAppRole)
}
if cfg.Vault.JWTPath != DefaultJWTPath {
t.Errorf("jwt_path = %q, want %q", cfg.Vault.JWTPath, DefaultJWTPath)
}
if cfg.Vault.OutputPath != "/tmp/certmanager" {
t.Errorf("output_path = %q, want it read from under vault:", cfg.Vault.OutputPath)
}
if !cfg.Vault.SkipTLSVerify() {
t.Error("tls_skip_verify must default to true")
}
d, err := cfg.Vault.RequestTimeout()
if err != nil || d != DefaultTimeout {
t.Errorf("RequestTimeout() = %v, %v, want %v", d, err, DefaultTimeout)
}
}
func TestLoadOverrides(t *testing.T) {
cfg, err := Load(writeConfig(t, approleBody+" tls_skip_verify: false\n timeout: 5s\n"))
if err != nil {
t.Fatalf("Load: %v", err)
}
if cfg.Vault.SkipTLSVerify() {
t.Error("tls_skip_verify: false must disable skipping")
}
d, err := cfg.Vault.RequestTimeout()
if err != nil || d != 5*time.Second {
t.Errorf("RequestTimeout() = %v, %v, want 5s", d, err)
}
}
func TestLoadKubernetes(t *testing.T) {
cfg, err := Load(writeConfig(t, `vault:
addr: https://vault.service.consul:8200
auth_method: kubernetes
k8s_mount: k8s/au/syd1
k8s_role: puppet_certmanager
mount_point: pki_int
role_name: servers_default
`))
if err != nil {
t.Fatalf("Load: %v", err)
}
if cfg.Vault.K8sMount != "k8s/au/syd1" || cfg.Vault.K8sRole != "puppet_certmanager" {
t.Errorf("kubernetes keys not parsed: %+v", cfg.Vault)
}
}
func TestLoadErrors(t *testing.T) {
tests := []struct {
name string
body string
wantErr string
}{
{"missing addr", "vault:\n mount_point: pki_int\n role_name: r\n role_id: x\n approle_path: approle\n", "vault.addr"},
{"missing mount_point", "vault:\n addr: https://v:8200\n role_name: r\n role_id: x\n approle_path: approle\n", "vault.mount_point"},
{"missing role_name", "vault:\n addr: https://v:8200\n mount_point: pki_int\n role_id: x\n approle_path: approle\n", "vault.role_name"},
{"missing role_id", "vault:\n addr: https://v:8200\n mount_point: pki_int\n role_name: r\n approle_path: approle\n", "vault.role_id"},
{"missing approle_path", "vault:\n addr: https://v:8200\n mount_point: pki_int\n role_name: r\n role_id: x\n", "vault.approle_path"},
{"missing k8s_mount", "vault:\n addr: https://v:8200\n auth_method: kubernetes\n k8s_role: r\n mount_point: pki_int\n role_name: r\n", "vault.k8s_mount"},
{"missing k8s_role", "vault:\n addr: https://v:8200\n auth_method: kubernetes\n k8s_mount: m\n mount_point: pki_int\n role_name: r\n", "vault.k8s_role"},
{"unknown auth method", "vault:\n addr: https://v:8200\n auth_method: token\n mount_point: pki_int\n role_name: r\n", `unknown vault.auth_method "token"`},
{"bad timeout", approleBody + " timeout: soon\n", "not a valid duration"},
{"zero timeout", approleBody + " timeout: 0s\n", "must be positive"},
{"malformed yaml", "vault: [\n", "parse config"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := Load(writeConfig(t, tc.body))
if err == nil {
t.Fatal("Load succeeded, want error")
}
if !strings.Contains(err.Error(), tc.wantErr) {
t.Errorf("error = %v, want it to mention %q", err, tc.wantErr)
}
})
}
}
func TestLoadMissingFile(t *testing.T) {
_, err := Load(filepath.Join(t.TempDir(), "absent.yaml"))
if err == nil || !strings.Contains(err.Error(), "open config") {
t.Fatalf("error = %v, want an open config failure", err)
}
}
func TestDefaultPath(t *testing.T) {
if got := DefaultPath(); got != "/opt/certmanager/config.yaml" {
t.Errorf("DefaultPath() = %q, want the Puppet-managed path", got)
}
}