mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
622 lines
18 KiB
Go
622 lines
18 KiB
Go
package admin
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// --- Key / Token generation ---
|
|
|
|
func TestGenerateAWSAccessKey_Format(t *testing.T) {
|
|
key := GenerateAWSAccessKey()
|
|
assert.Len(t, key, 20)
|
|
assert.True(t, strings.HasPrefix(key, "AKIA"))
|
|
for _, c := range key[4:] {
|
|
assert.True(t, (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'),
|
|
"unexpected character %c in access key suffix", c)
|
|
}
|
|
}
|
|
|
|
func TestGenerateAWSAccessKey_Uniqueness(t *testing.T) {
|
|
assert.NotEqual(t, GenerateAWSAccessKey(), GenerateAWSAccessKey())
|
|
}
|
|
|
|
func TestGenerateAWSSecretKey_Format(t *testing.T) {
|
|
key := GenerateAWSSecretKey()
|
|
assert.Len(t, key, 40)
|
|
_, err := base64.StdEncoding.DecodeString(key)
|
|
assert.NoError(t, err, "secret key should be valid base64")
|
|
}
|
|
|
|
func TestGenerateAWSSecretKey_Uniqueness(t *testing.T) {
|
|
assert.NotEqual(t, GenerateAWSSecretKey(), GenerateAWSSecretKey())
|
|
}
|
|
|
|
func TestGenerateAccountID_Format(t *testing.T) {
|
|
id := GenerateAccountID()
|
|
assert.Len(t, id, 12)
|
|
for _, c := range id {
|
|
assert.True(t, c >= '0' && c <= '9', "expected digit, got %c", c)
|
|
}
|
|
// First digit should not be 0
|
|
assert.NotEqual(t, byte('0'), id[0], "12-digit account ID should not start with 0")
|
|
}
|
|
|
|
func TestGenerateAccountID_Uniqueness(t *testing.T) {
|
|
assert.NotEqual(t, GenerateAccountID(), GenerateAccountID())
|
|
}
|
|
|
|
func TestGenerateNATSToken_Format(t *testing.T) {
|
|
token := GenerateNATSToken()
|
|
assert.True(t, strings.HasPrefix(token, "nats_"))
|
|
assert.Len(t, token, 37) // 5 prefix + 32 random
|
|
// URL-safe base64: no '+' or '/'
|
|
assert.NotContains(t, token, "+")
|
|
assert.NotContains(t, token, "/")
|
|
}
|
|
|
|
func TestGenerateNATSToken_Uniqueness(t *testing.T) {
|
|
assert.NotEqual(t, GenerateNATSToken(), GenerateNATSToken())
|
|
}
|
|
|
|
// --- Config file generation ---
|
|
|
|
func TestGenerateConfigFile_Success(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "test.conf")
|
|
|
|
tmpl := "region = {{.Region}}\nnode = {{.Node}}"
|
|
settings := ConfigSettings{Region: "us-east-1", Node: "node1"}
|
|
|
|
err := GenerateConfigFile(path, tmpl, settings)
|
|
require.NoError(t, err)
|
|
|
|
data, err := os.ReadFile(path)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(data), "region = us-east-1")
|
|
assert.Contains(t, string(data), "node = node1")
|
|
|
|
info, _ := os.Stat(path)
|
|
assert.Equal(t, os.FileMode(0600), info.Mode().Perm())
|
|
}
|
|
|
|
func TestGenerateConfigFile_InvalidTemplate(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "bad.conf")
|
|
err := GenerateConfigFile(path, "{{.Unclosed", ConfigSettings{})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to parse template")
|
|
}
|
|
|
|
func TestGenerateConfigFile_InvalidPath(t *testing.T) {
|
|
err := GenerateConfigFile("/nonexistent/dir/file.conf", "ok", ConfigSettings{})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to create config file")
|
|
}
|
|
|
|
func TestGenerateConfigFile_OverwritesExisting(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "overwrite.conf")
|
|
|
|
require.NoError(t, GenerateConfigFile(path, "old={{.Region}}", ConfigSettings{Region: "old"}))
|
|
require.NoError(t, GenerateConfigFile(path, "new={{.Region}}", ConfigSettings{Region: "new"}))
|
|
|
|
data, _ := os.ReadFile(path)
|
|
assert.Contains(t, string(data), "new=new")
|
|
assert.NotContains(t, string(data), "old")
|
|
}
|
|
|
|
func TestGenerateConfigFiles_AllSucceed(t *testing.T) {
|
|
dir := t.TempDir()
|
|
configs := []ConfigFile{
|
|
{Name: "a", Path: filepath.Join(dir, "a.conf"), Template: "a={{.Region}}"},
|
|
{Name: "b", Path: filepath.Join(dir, "b.conf"), Template: "b={{.Node}}"},
|
|
}
|
|
err := GenerateConfigFiles(configs, ConfigSettings{Region: "us-west-2", Node: "n1"})
|
|
require.NoError(t, err)
|
|
|
|
for _, cfg := range configs {
|
|
assert.True(t, FileExists(cfg.Path))
|
|
}
|
|
}
|
|
|
|
func TestGenerateConfigFiles_StopsOnFirstError(t *testing.T) {
|
|
dir := t.TempDir()
|
|
configs := []ConfigFile{
|
|
{Name: "ok", Path: filepath.Join(dir, "ok.conf"), Template: "ok"},
|
|
{Name: "bad", Path: "/nonexistent/dir/bad.conf", Template: "bad"},
|
|
{Name: "never", Path: filepath.Join(dir, "never.conf"), Template: "never"},
|
|
}
|
|
err := GenerateConfigFiles(configs, ConfigSettings{})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "bad")
|
|
assert.False(t, FileExists(filepath.Join(dir, "never.conf")))
|
|
}
|
|
|
|
// --- FileExists ---
|
|
|
|
func TestFileExists(t *testing.T) {
|
|
dir := t.TempDir()
|
|
existing := filepath.Join(dir, "exists.txt")
|
|
require.NoError(t, os.WriteFile(existing, []byte("hi"), 0644))
|
|
|
|
assert.True(t, FileExists(existing))
|
|
assert.False(t, FileExists(filepath.Join(dir, "nope.txt")))
|
|
assert.True(t, FileExists(dir)) // directory also returns true
|
|
}
|
|
|
|
// --- UpdateAWSINIFile ---
|
|
|
|
func TestUpdateAWSINIFile_CreateNew(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "credentials")
|
|
|
|
err := UpdateAWSINIFile(path, "hive", map[string]string{
|
|
"aws_access_key_id": "AKIATEST",
|
|
"aws_secret_access_key": "secrettest",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
data, _ := os.ReadFile(path)
|
|
content := string(data)
|
|
assert.Contains(t, content, "[hive]")
|
|
assert.Contains(t, content, "AKIATEST")
|
|
assert.Contains(t, content, "secrettest")
|
|
}
|
|
|
|
func TestUpdateAWSINIFile_UpdateExisting(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "credentials")
|
|
|
|
// Create with initial value
|
|
require.NoError(t, UpdateAWSINIFile(path, "hive", map[string]string{"key": "old"}))
|
|
// Update
|
|
require.NoError(t, UpdateAWSINIFile(path, "hive", map[string]string{"key": "new"}))
|
|
|
|
data, _ := os.ReadFile(path)
|
|
content := string(data)
|
|
assert.Contains(t, content, "new")
|
|
}
|
|
|
|
func TestUpdateAWSINIFile_AddNewSection(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "credentials")
|
|
|
|
require.NoError(t, UpdateAWSINIFile(path, "default", map[string]string{"key": "default-val"}))
|
|
require.NoError(t, UpdateAWSINIFile(path, "hive", map[string]string{"key": "hive-val"}))
|
|
|
|
data, _ := os.ReadFile(path)
|
|
content := string(data)
|
|
assert.Contains(t, content, "[default]")
|
|
assert.Contains(t, content, "[hive]")
|
|
assert.Contains(t, content, "default-val")
|
|
assert.Contains(t, content, "hive-val")
|
|
}
|
|
|
|
// --- SetupAWSCredentials ---
|
|
|
|
func TestSetupAWSCredentials_CreatesFiles(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("HOME", dir)
|
|
|
|
err := SetupAWSCredentials("AKIATEST123", "secret123", "us-east-1", "/path/to/ca.pem", "")
|
|
require.NoError(t, err)
|
|
|
|
credData, _ := os.ReadFile(filepath.Join(dir, ".aws", "credentials"))
|
|
configData, _ := os.ReadFile(filepath.Join(dir, ".aws", "config"))
|
|
|
|
assert.Contains(t, string(credData), "AKIATEST123")
|
|
assert.Contains(t, string(credData), "secret123")
|
|
assert.Contains(t, string(configData), "us-east-1")
|
|
assert.Contains(t, string(configData), "https://localhost:9999")
|
|
assert.Contains(t, string(configData), "/path/to/ca.pem")
|
|
}
|
|
|
|
func TestSetupAWSCredentials_PreservesExistingProfiles(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("HOME", dir)
|
|
|
|
awsDir := filepath.Join(dir, ".aws")
|
|
require.NoError(t, os.MkdirAll(awsDir, 0700))
|
|
require.NoError(t, UpdateAWSINIFile(filepath.Join(awsDir, "credentials"), "default", map[string]string{
|
|
"aws_access_key_id": "EXISTING_KEY",
|
|
}))
|
|
|
|
err := SetupAWSCredentials("NEWAKEY", "NEWSECRET", "us-west-2", "/ca.pem", "")
|
|
require.NoError(t, err)
|
|
|
|
data, _ := os.ReadFile(filepath.Join(awsDir, "credentials"))
|
|
content := string(data)
|
|
assert.Contains(t, content, "EXISTING_KEY")
|
|
assert.Contains(t, content, "NEWAKEY")
|
|
}
|
|
|
|
func TestSetupAWSCredentials_UsesBindIP(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("HOME", dir)
|
|
|
|
err := SetupAWSCredentials("AKIATEST123", "secret123", "us-east-1", "/ca.pem", "10.11.12.1")
|
|
require.NoError(t, err)
|
|
|
|
configData, _ := os.ReadFile(filepath.Join(dir, ".aws", "config"))
|
|
assert.Contains(t, string(configData), "https://10.11.12.1:9999")
|
|
assert.NotContains(t, string(configData), "localhost")
|
|
}
|
|
|
|
func TestSetupAWSCredentials_FallsBackToLocalhostForWildcard(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("HOME", dir)
|
|
|
|
err := SetupAWSCredentials("AKIATEST123", "secret123", "us-east-1", "/ca.pem", "0.0.0.0")
|
|
require.NoError(t, err)
|
|
|
|
configData, _ := os.ReadFile(filepath.Join(dir, ".aws", "config"))
|
|
assert.Contains(t, string(configData), "https://localhost:9999")
|
|
}
|
|
|
|
// --- Certificate generation ---
|
|
|
|
func TestGenerateCACert_CreatesValidCA(t *testing.T) {
|
|
t.Parallel()
|
|
dir := t.TempDir()
|
|
certPath := filepath.Join(dir, "ca.pem")
|
|
keyPath := filepath.Join(dir, "ca.key")
|
|
|
|
err := GenerateCACert(certPath, keyPath)
|
|
require.NoError(t, err)
|
|
|
|
// Parse certificate
|
|
certPEM, _ := os.ReadFile(certPath)
|
|
block, _ := pem.Decode(certPEM)
|
|
require.NotNil(t, block)
|
|
assert.Equal(t, "CERTIFICATE", block.Type)
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
require.NoError(t, err)
|
|
assert.True(t, cert.IsCA)
|
|
assert.Equal(t, "Hive Local CA", cert.Subject.CommonName)
|
|
assert.NotZero(t, cert.KeyUsage&x509.KeyUsageCertSign)
|
|
|
|
// Parse key
|
|
keyPEM, _ := os.ReadFile(keyPath)
|
|
keyBlock, _ := pem.Decode(keyPEM)
|
|
require.NotNil(t, keyBlock)
|
|
assert.Equal(t, "PRIVATE KEY", keyBlock.Type)
|
|
|
|
// Verify key file permissions
|
|
info, _ := os.Stat(keyPath)
|
|
assert.Equal(t, os.FileMode(0600), info.Mode().Perm())
|
|
}
|
|
|
|
// TestGenerateSignedCert uses subtests sharing a single CA to avoid repeated 4096-bit key generation.
|
|
func TestGenerateSignedCert(t *testing.T) {
|
|
t.Parallel()
|
|
// Generate CA once for all subtests (~0.7s instead of ~0.7s x 3)
|
|
caDir := t.TempDir()
|
|
caCertPath := filepath.Join(caDir, "ca.pem")
|
|
caKeyPath := filepath.Join(caDir, "ca.key")
|
|
require.NoError(t, GenerateCACert(caCertPath, caKeyPath))
|
|
|
|
t.Run("CreatesValidCert", func(t *testing.T) {
|
|
t.Parallel()
|
|
dir := t.TempDir()
|
|
certPath := filepath.Join(dir, "server.pem")
|
|
keyPath := filepath.Join(dir, "server.key")
|
|
|
|
require.NoError(t, GenerateSignedCert(certPath, keyPath, caCertPath, caKeyPath))
|
|
|
|
certPEM, _ := os.ReadFile(certPath)
|
|
block, _ := pem.Decode(certPEM)
|
|
require.NotNil(t, block)
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
require.NoError(t, err)
|
|
assert.False(t, cert.IsCA)
|
|
assert.Equal(t, "localhost", cert.Subject.CommonName)
|
|
assert.Contains(t, cert.DNSNames, "localhost")
|
|
|
|
hasLoopback := false
|
|
for _, ip := range cert.IPAddresses {
|
|
if ip.Equal(net.ParseIP("127.0.0.1")) {
|
|
hasLoopback = true
|
|
}
|
|
}
|
|
assert.True(t, hasLoopback)
|
|
|
|
// Verify against CA
|
|
caCertPEM, _ := os.ReadFile(caCertPath)
|
|
caBlock, _ := pem.Decode(caCertPEM)
|
|
caCert, _ := x509.ParseCertificate(caBlock.Bytes)
|
|
pool := x509.NewCertPool()
|
|
pool.AddCert(caCert)
|
|
_, err = cert.Verify(x509.VerifyOptions{Roots: pool})
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := os.Stat(keyPath)
|
|
assert.Equal(t, os.FileMode(0600), info.Mode().Perm())
|
|
})
|
|
|
|
t.Run("ExtraIPs", func(t *testing.T) {
|
|
t.Parallel()
|
|
dir := t.TempDir()
|
|
certPath := filepath.Join(dir, "server.pem")
|
|
keyPath := filepath.Join(dir, "server.key")
|
|
|
|
require.NoError(t, GenerateSignedCert(certPath, keyPath, caCertPath, caKeyPath, "192.168.1.100"))
|
|
|
|
certPEM, _ := os.ReadFile(certPath)
|
|
block, _ := pem.Decode(certPEM)
|
|
cert, _ := x509.ParseCertificate(block.Bytes)
|
|
|
|
hasExtraIP := false
|
|
for _, ip := range cert.IPAddresses {
|
|
if ip.Equal(net.ParseIP("192.168.1.100")) {
|
|
hasExtraIP = true
|
|
}
|
|
}
|
|
assert.True(t, hasExtraIP, "cert should contain extra IP 192.168.1.100")
|
|
})
|
|
|
|
t.Run("SkipsDuplicateAndSpecialIPs", func(t *testing.T) {
|
|
t.Parallel()
|
|
dir := t.TempDir()
|
|
certPath := filepath.Join(dir, "server.pem")
|
|
keyPath := filepath.Join(dir, "server.key")
|
|
|
|
require.NoError(t, GenerateSignedCert(certPath, keyPath, caCertPath, caKeyPath, "127.0.0.1", "::1", "0.0.0.0", ""))
|
|
|
|
certPEM, _ := os.ReadFile(certPath)
|
|
block, _ := pem.Decode(certPEM)
|
|
cert, _ := x509.ParseCertificate(block.Bytes)
|
|
|
|
assert.Len(t, cert.IPAddresses, 2)
|
|
})
|
|
|
|
t.Run("InvalidCACert", func(t *testing.T) {
|
|
t.Parallel()
|
|
dir := t.TempDir()
|
|
badCACert := filepath.Join(dir, "bad-ca.pem")
|
|
require.NoError(t, os.WriteFile(badCACert, []byte("not-a-cert"), 0600))
|
|
|
|
err := GenerateSignedCert(filepath.Join(dir, "s.pem"), filepath.Join(dir, "s.key"), badCACert, caKeyPath)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestGenerateSelfSignedCert_CreatesValidCert(t *testing.T) {
|
|
t.Parallel()
|
|
dir := t.TempDir()
|
|
certPath := filepath.Join(dir, "self.pem")
|
|
keyPath := filepath.Join(dir, "self.key")
|
|
|
|
require.NoError(t, GenerateSelfSignedCert(certPath, keyPath))
|
|
|
|
certPEM, _ := os.ReadFile(certPath)
|
|
block, _ := pem.Decode(certPEM)
|
|
require.NotNil(t, block)
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, cert.DNSNames, "localhost")
|
|
|
|
info, _ := os.Stat(keyPath)
|
|
assert.Equal(t, os.FileMode(0600), info.Mode().Perm())
|
|
}
|
|
|
|
// --- Certificate orchestrator ---
|
|
|
|
// TestGenerateCertificatesIfNeeded uses subtests to share the initial generation.
|
|
func TestGenerateCertificatesIfNeeded(t *testing.T) {
|
|
t.Parallel()
|
|
dir := t.TempDir()
|
|
|
|
// First call generates all certs
|
|
caCertPath := GenerateCertificatesIfNeeded(dir, false, "")
|
|
assert.Equal(t, filepath.Join(dir, "ca.pem"), caCertPath)
|
|
assert.True(t, FileExists(filepath.Join(dir, "ca.pem")))
|
|
assert.True(t, FileExists(filepath.Join(dir, "ca.key")))
|
|
assert.True(t, FileExists(filepath.Join(dir, "server.pem")))
|
|
assert.True(t, FileExists(filepath.Join(dir, "server.key")))
|
|
|
|
t.Run("SkipsWhenAllExist", func(t *testing.T) {
|
|
caInfo, _ := os.Stat(filepath.Join(dir, "ca.pem"))
|
|
origModTime := caInfo.ModTime()
|
|
|
|
GenerateCertificatesIfNeeded(dir, false, "")
|
|
caInfo2, _ := os.Stat(filepath.Join(dir, "ca.pem"))
|
|
assert.Equal(t, origModTime, caInfo2.ModTime())
|
|
})
|
|
|
|
t.Run("ForceRegenerates", func(t *testing.T) {
|
|
origCA, _ := os.ReadFile(filepath.Join(dir, "ca.pem"))
|
|
|
|
GenerateCertificatesIfNeeded(dir, true, "")
|
|
newCA, _ := os.ReadFile(filepath.Join(dir, "ca.pem"))
|
|
assert.NotEqual(t, origCA, newCA)
|
|
})
|
|
}
|
|
|
|
func TestGenerateServerCertOnly(t *testing.T) {
|
|
t.Parallel()
|
|
// Generate CA once, reuse for subtests
|
|
caDir := t.TempDir()
|
|
require.NoError(t, GenerateCACert(filepath.Join(caDir, "ca.pem"), filepath.Join(caDir, "ca.key")))
|
|
|
|
t.Run("Success", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
// Copy CA files into test dir
|
|
caCert, _ := os.ReadFile(filepath.Join(caDir, "ca.pem"))
|
|
caKey, _ := os.ReadFile(filepath.Join(caDir, "ca.key"))
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "ca.pem"), caCert, 0600))
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "ca.key"), caKey, 0600))
|
|
|
|
err := GenerateServerCertOnly(dir, "10.0.0.5")
|
|
require.NoError(t, err)
|
|
assert.True(t, FileExists(filepath.Join(dir, "server.pem")))
|
|
assert.True(t, FileExists(filepath.Join(dir, "server.key")))
|
|
|
|
certPEM, _ := os.ReadFile(filepath.Join(dir, "server.pem"))
|
|
block, _ := pem.Decode(certPEM)
|
|
cert, _ := x509.ParseCertificate(block.Bytes)
|
|
|
|
hasBindIP := false
|
|
for _, ip := range cert.IPAddresses {
|
|
if ip.Equal(net.ParseIP("10.0.0.5")) {
|
|
hasBindIP = true
|
|
}
|
|
}
|
|
assert.True(t, hasBindIP)
|
|
})
|
|
|
|
t.Run("MissingCA", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
err := GenerateServerCertOnly(dir, "10.0.0.5")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "CA files not found")
|
|
})
|
|
}
|
|
|
|
// --- Directory creation ---
|
|
|
|
func TestCreateServiceDirectories_CreatesAll(t *testing.T) {
|
|
dir := t.TempDir()
|
|
CreateServiceDirectories(dir)
|
|
|
|
expected := []string{"images", "amis", "volumes", "state", "logs", "nats", "predastore", "viperblock", "hive"}
|
|
for _, name := range expected {
|
|
path := filepath.Join(dir, name)
|
|
info, err := os.Stat(path)
|
|
assert.NoError(t, err, "directory %s should exist", name)
|
|
if err == nil {
|
|
assert.True(t, info.IsDir())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCreateServiceDirectories_Idempotent(t *testing.T) {
|
|
dir := t.TempDir()
|
|
CreateServiceDirectories(dir)
|
|
// Should not error on second call
|
|
CreateServiceDirectories(dir)
|
|
|
|
info, err := os.Stat(filepath.Join(dir, "images"))
|
|
assert.NoError(t, err)
|
|
assert.True(t, info.IsDir())
|
|
}
|
|
|
|
// --- Predastore multi-node config ---
|
|
|
|
func TestGenerateMultiNodePredastoreConfig_Success(t *testing.T) {
|
|
tmpl := `{{range .Nodes}}[[db]]
|
|
id = {{.ID}}
|
|
host = "{{.Host}}"
|
|
{{end}}`
|
|
nodes := []PredastoreNodeConfig{
|
|
{ID: 1, Host: "10.0.0.1"},
|
|
{ID: 2, Host: "10.0.0.2"},
|
|
{ID: 3, Host: "10.0.0.3"},
|
|
}
|
|
|
|
result, err := GenerateMultiNodePredastoreConfig(tmpl, nodes, "AK", "SK", "us-east-1")
|
|
require.NoError(t, err)
|
|
assert.Contains(t, result, `host = "10.0.0.1"`)
|
|
assert.Contains(t, result, `host = "10.0.0.3"`)
|
|
}
|
|
|
|
func TestGenerateMultiNodePredastoreConfig_MinimumNodes(t *testing.T) {
|
|
tmpl := "{{range .Nodes}}{{.ID}}{{end}}"
|
|
|
|
_, err := GenerateMultiNodePredastoreConfig(tmpl, []PredastoreNodeConfig{
|
|
{ID: 1, Host: "10.0.0.1"},
|
|
{ID: 2, Host: "10.0.0.2"},
|
|
}, "AK", "SK", "us-east-1")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "at least 3 nodes")
|
|
}
|
|
|
|
func TestGenerateMultiNodePredastoreConfig_InvalidTemplate(t *testing.T) {
|
|
_, err := GenerateMultiNodePredastoreConfig("{{.Unclosed", []PredastoreNodeConfig{
|
|
{ID: 1, Host: "a"}, {ID: 2, Host: "b"}, {ID: 3, Host: "c"},
|
|
}, "AK", "SK", "us-east-1")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to parse")
|
|
}
|
|
|
|
// --- FindNodeIDByIP ---
|
|
|
|
func TestFindNodeIDByIP(t *testing.T) {
|
|
nodes := []PredastoreNodeConfig{
|
|
{ID: 1, Host: "10.0.0.1"},
|
|
{ID: 2, Host: "10.0.0.2"},
|
|
{ID: 3, Host: "10.0.0.3"},
|
|
}
|
|
|
|
assert.Equal(t, 2, FindNodeIDByIP(nodes, "10.0.0.2"))
|
|
assert.Equal(t, 0, FindNodeIDByIP(nodes, "10.0.0.99"))
|
|
assert.Equal(t, 0, FindNodeIDByIP(nil, "10.0.0.1"))
|
|
}
|
|
|
|
// --- ParsePredastoreNodeIDFromConfig ---
|
|
|
|
func TestParsePredastoreNodeIDFromConfig(t *testing.T) {
|
|
tomlContent := `
|
|
[[db]]
|
|
id = 1
|
|
host = "10.0.0.1"
|
|
|
|
[[db]]
|
|
id = 2
|
|
host = "10.0.0.2"
|
|
|
|
[[db]]
|
|
id = 3
|
|
host = "10.0.0.3"
|
|
`
|
|
assert.Equal(t, 2, ParsePredastoreNodeIDFromConfig(tomlContent, "10.0.0.2"))
|
|
assert.Equal(t, 0, ParsePredastoreNodeIDFromConfig(tomlContent, "10.0.0.99"))
|
|
assert.Equal(t, 0, ParsePredastoreNodeIDFromConfig("invalid toml {{{", "10.0.0.1"))
|
|
assert.Equal(t, 0, ParsePredastoreNodeIDFromConfig("", "10.0.0.1"))
|
|
}
|
|
|
|
// --- Integration: Full config generation flow ---
|
|
|
|
func TestGenerateConfigFile_HiveTomlTemplate(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "hive.toml")
|
|
|
|
tmpl := `version = "1.0"
|
|
epoch = 1
|
|
node = "{{.Node}}"
|
|
|
|
[nodes.{{.Node}}]
|
|
region = "{{.Region}}"
|
|
accesskey = "{{.AccessKey}}"
|
|
secretkey = "{{.SecretKey}}"
|
|
`
|
|
|
|
settings := ConfigSettings{
|
|
Node: "node1",
|
|
Region: "us-east-1",
|
|
AccessKey: "AKIATEST",
|
|
SecretKey: "SECRET",
|
|
}
|
|
|
|
require.NoError(t, GenerateConfigFile(path, tmpl, settings))
|
|
|
|
data, _ := os.ReadFile(path)
|
|
content := string(data)
|
|
assert.Contains(t, content, `node = "node1"`)
|
|
assert.Contains(t, content, `region = "us-east-1"`)
|
|
assert.Contains(t, content, fmt.Sprintf(`accesskey = "%s"`, settings.AccessKey))
|
|
}
|