221 lines
6.3 KiB
Go
221 lines
6.3 KiB
Go
package integrationtest
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"git.wamblee.org/converge/pkg/support/ioutils"
|
|
"git.wamblee.org/converge/pkg/support/sshsupport"
|
|
"git.wamblee.org/converge/pkg/testsupport"
|
|
"github.com/stretchr/testify/suite"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/network"
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
agentEntryPointTemplate = `
|
|
curl -v http://converge:8000/downloads/agent > agent
|
|
chmod 755 agent
|
|
echo "{{.publicKey}}" > .authorized_keys
|
|
cat .authorized_keys
|
|
echo "HOSTNAME=$( hostname )"
|
|
./agent --id abc ws://converge:8000
|
|
tail -f /dev/null `
|
|
|
|
clientEntryPointTemplate = `
|
|
curl -v http://converge:8000/downloads/wsproxy > wsproxy
|
|
chmod 755 wsproxy
|
|
echo "{{.privateKey}}" > id_rsa
|
|
chmod 400 id_rsa
|
|
echo "===================================================================================================="
|
|
echo "
|
|
hostname | sed 's/^/=============== HOSTNAME_OF_AGENT_ON_CLIENT=/g' " |
|
|
ssh -tt -oStrictHostKeyChecking=no -i id_rsa -oProxyCommand="./wsproxy ws://converge:8000/client/abc" localhost
|
|
echo "===================================================================================================="
|
|
echo Ready
|
|
sleep 1000000`
|
|
)
|
|
|
|
var (
|
|
convergeImage = fmt.Sprintf("%s/converge:1.0", getRegistry())
|
|
agentImage = fmt.Sprintf("%s/converge-test:1.0", getRegistry())
|
|
clientImage = agentImage
|
|
)
|
|
|
|
func getRegistry() string {
|
|
registry, ok := os.LookupEnv("REGISTRY")
|
|
if ok {
|
|
return registry
|
|
}
|
|
moduleDir := testsupport.GetGoModDIr()
|
|
log.Printf("Go module directory %s", moduleDir)
|
|
envFile := filepath.Join(moduleDir, ".env")
|
|
file, err := os.Open(envFile)
|
|
if err != nil {
|
|
panic("Could not find .env file and REGISTRY env var is not set")
|
|
}
|
|
defer file.Close()
|
|
props, err := ioutils.ReadPropertyFile(file)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Error reading environment file '%v': %v", envFile, err))
|
|
}
|
|
registry, ok = props["REGISTRY"]
|
|
if !ok {
|
|
panic(fmt.Sprintf("Could not find REGISTRY setting in property file '%v'", envFile))
|
|
}
|
|
return registry
|
|
}
|
|
|
|
type IntegrationTestSuite struct {
|
|
suite.Suite
|
|
|
|
ctx context.Context
|
|
cancelFunc context.CancelFunc
|
|
}
|
|
|
|
func Test_IntergrationTestSuite(t *testing.T) {
|
|
suite.Run(t, &IntegrationTestSuite{})
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) SetupSuite() {
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) TearDownSuite() {
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) SetupTest() {
|
|
ctx, cancelFunc := testsupport.CreateTestContext(context.Background(), 10*time.Second)
|
|
s.ctx = ctx
|
|
s.cancelFunc = cancelFunc
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) TearDownTest() {
|
|
s.cancelFunc()
|
|
}
|
|
|
|
type ContainerLogger func(logtype string, msg string)
|
|
|
|
func logContainerOutput(container string, logtype string, msg string) {
|
|
log.Printf("%s: %s: %s", container, logtype, msg)
|
|
}
|
|
|
|
func containerLogger(container string) ContainerLogger {
|
|
return func(logtype string, msg string) {
|
|
logContainerOutput(container, logtype, msg)
|
|
}
|
|
}
|
|
|
|
func (c ContainerLogger) Accept(log testcontainers.Log) {
|
|
c(log.LogType, string(log.Content))
|
|
}
|
|
|
|
func createLogConsumerConfig(container string) *testcontainers.LogConsumerConfig {
|
|
return &testcontainers.LogConsumerConfig{
|
|
Opts: []testcontainers.LogProductionOption{testcontainers.WithLogProductionTimeout(10 * time.Second)},
|
|
Consumers: []testcontainers.LogConsumer{containerLogger(container)},
|
|
}
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) runCmd(container testcontainers.Container, cmd []string) (int, string, error) {
|
|
status, reader, err := container.Exec(s.ctx, cmd)
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
output, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
return status, string(output), nil
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) runCmdFailOnError(container testcontainers.Container, cmd []string) (int, string) {
|
|
status, output, err := s.runCmd(container, cmd)
|
|
s.Require().Nil(err)
|
|
return status, output
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) defineContainer(net *testcontainers.DockerNetwork, hostname string,
|
|
image string,
|
|
waitForLog string,
|
|
ports []string) testcontainers.GenericContainerRequest {
|
|
|
|
log.Printf("PORTS %v", ports)
|
|
|
|
request := testcontainers.GenericContainerRequest{
|
|
ContainerRequest: testcontainers.ContainerRequest{
|
|
Image: image,
|
|
ExposedPorts: ports,
|
|
Hostname: hostname,
|
|
WaitingFor: wait.ForLog(waitForLog).WithStartupTimeout(10 * time.Second),
|
|
LogConsumerCfg: createLogConsumerConfig(hostname),
|
|
Networks: []string{net.Name},
|
|
},
|
|
Started: false,
|
|
}
|
|
return request
|
|
}
|
|
|
|
func (s *IntegrationTestSuite) Test_SingleAgentAndClient() {
|
|
|
|
net, err := network.New(s.ctx)
|
|
s.Nil(err)
|
|
|
|
// SSH key to use
|
|
privateKey, publicKey, err := sshsupport.GeneratePrivatePublicKey(2048)
|
|
s.Require().Nil(err)
|
|
|
|
// The server
|
|
|
|
converge, err := testcontainers.GenericContainer(s.ctx,
|
|
s.defineContainer(net, "converge",
|
|
convergeImage,
|
|
"Rendez-vous server listening",
|
|
[]string{"8000/tcp"}))
|
|
s.Require().Nil(err)
|
|
err = converge.Start(s.ctx)
|
|
s.Require().Nil(err)
|
|
port, err := converge.MappedPort(s.ctx, "8000/tcp")
|
|
s.Require().Nil(err)
|
|
log.Printf("External port %v", port)
|
|
|
|
// The agent
|
|
|
|
agentEntrypoint := testsupport.Template(agentEntryPointTemplate,
|
|
map[string]string{"publicKey": publicKey})
|
|
|
|
agentRequest := s.defineContainer(net, "agent", agentImage,
|
|
"starting ssh server, waiting for debug sessions", []string{})
|
|
agentRequest.Entrypoint = []string{
|
|
"sh", "-c", agentEntrypoint}
|
|
agent, err := testcontainers.GenericContainer(s.ctx, agentRequest)
|
|
s.Require().Nil(err)
|
|
s.Require().Nil(agent.Start(s.ctx))
|
|
|
|
// The client
|
|
|
|
clientEntrypoint := testsupport.Template(clientEntryPointTemplate,
|
|
map[string]string{"privateKey": privateKey})
|
|
clientRequest := s.defineContainer(net, "client", clientImage,
|
|
"Ready", []string{})
|
|
clientRequest.Entrypoint = []string{
|
|
"sh", "-c", clientEntrypoint}
|
|
client, err := testcontainers.GenericContainer(s.ctx, clientRequest)
|
|
s.Require().Nil(err)
|
|
s.Require().Nil(client.Start(s.ctx))
|
|
|
|
input, err := client.Logs(s.ctx)
|
|
s.Require().Nil(err)
|
|
inputBytes, err := io.ReadAll(input)
|
|
s.Require().Nil(err)
|
|
s.True(strings.Contains(string(inputBytes), "HOSTNAME_OF_AGENT_ON_CLIENT=agent"))
|
|
|
|
log.Printf("Found registry: %v", getRegistry())
|
|
}
|