Skip to content

Commit

Permalink
Factor out env vars the same way as Complement
Browse files Browse the repository at this point in the history
  • Loading branch information
kegsay committed Jan 10, 2024
1 parent f94ff81 commit 0509a2c
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 42 deletions.
19 changes: 19 additions & 0 deletions ENVIRONMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
*This file is automatically generated via ./cmd/gendoc*

## Complement-Crypto Configuration
Complement is configured exclusively through the use of environment variables. These variables are described below. Additional environment variables can be used, and are outlined at https://github.com/matrix-org/complement/blob/main/ENVIRONMENT.md

#### `COMPLEMENT_CRYPTO_TCPDUMP`
If 1, automatically attempts to run `tcpdump` when the containers are running. Stops dumping when tests complete. This will probably require you to run `go test` with `sudo -E`. The `.pcap` file is written to `tests/test.pcap`.
- Type: `bool`
- Default: 0

#### `COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX`
The client test matrix to run. Every test is run for each given permutation. The default matrix tests all JS/Rust permutations _ignoring federation_. Valid values are: - `j`: Run a JS SDK client on hs1. - `r`: Run a Rust SDK FFI client on hs1. - `J`: Run a JS SDK client on hs2. - `R`: Run a Rust SDK FFI client on hs2. TODO: needs additional SS proxy / postgres. For example, for a simple "Alice and Bob" test: - `rj,rr`: Run the test twice. Run 1: Alice=rust, Bob=JS. Run 2: Alice=rust, Bob=rust. All on HS1. - `jJ`: Run the test once. Run 1: Alice=JS on HS1, Bob=JS on HS2. Tests federation.
- Type: `[][]ClientType`
- Default: jj,jr,rj,rr

#### `COMPLEMENT_CRYPTO_WRITE_CONTAINER_LOGS`
If 1, writes container logs to ./tests. Useful as a debugging tool.
- Type: `bool`
- Default: 0
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,19 @@ cargo install uniffi-bindgen-go --path ./uniffi-bindgen-go/bindgen
- Sanity check compile `LIBRARY_PATH="$LIBRARY_PATH:/path/to/matrix-rust-sdk/target/debug" go test -c ./tests`


### Architecture

```
Host | dockerd
| +----------+ +----------+
| .--> | ss proxy | <--> | postgres |
+----------+ | +-----------+ | +-----+----+ +----------+
| Go tests | <--|--> | mitmproxy | <--+--> | hs1 |
+----------+ | +-----------+ | +-----+
| `--> | hs2 |
| +-----+
```

### Github Action (TODO)

Inputs:
Expand Down
132 changes: 132 additions & 0 deletions cmd/gendoc/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package main

import (
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"sort"
"strings"
)

var configPath = flag.String("config", "internal/config/config.go", "The path to internal/config/config.go")

type VarDoc struct {
Name string
Description string
Default string
Type string
}

func NewVarDoc(docstring string) (vd VarDoc) {
lines := strings.Split(docstring, "\n")
isDescription := false
for _, l := range lines {
if strings.HasPrefix(l, "Name:") {
isDescription = false
vd.Name = strings.TrimSpace(strings.TrimPrefix(l, "Name:"))
}
if strings.HasPrefix(l, "Default:") {
isDescription = false
vd.Default = strings.TrimSpace(strings.TrimPrefix(l, "Default:"))
}
if strings.HasPrefix(l, "Description:") {
l = strings.TrimPrefix(l, "Description:")
isDescription = true
}
if isDescription {
vd.Description += strings.TrimSpace(l) + " "
}
}
return
}

func findComplementStruct(path string) *ast.StructType {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
log.Fatal(err)
}
var complementConfigType *ast.TypeSpec
FindStruct:
for _, d := range node.Decls {
typeNode, ok := d.(*ast.GenDecl)
if !ok || typeNode.Tok != token.TYPE { // we want `type` keywords
continue
}
for _, s := range typeNode.Specs {
typeSpec, ok := s.(*ast.TypeSpec)
if !ok {
continue
}
if typeSpec.Name.Name == "ComplementCrypto" {
complementConfigType = typeSpec
break FindStruct
}
}
}
sType, ok := complementConfigType.Type.(*ast.StructType)
if !ok {
return nil
}
return sType
}

func typeForExpr(ex ast.Expr) string {
switch typeDecl := ex.(type) {
case *ast.Ident:
return typeDecl.Name
case *ast.SelectorExpr:
return typeDecl.Sel.Name
case *ast.ArrayType:
return "[]" + typeForExpr(typeDecl.Elt)
case *ast.MapType:
return "map[" + typeForExpr(typeDecl.Key) + "]" + typeForExpr(typeDecl.Value)
default:
return "-"
}
}

func main() {
flag.Parse()
if *configPath == "" {
flag.Usage()
os.Exit(1)
}
complement := findComplementStruct(*configPath)
if complement == nil {
log.Fatal("file does not contain type ComplementCrypto struct {...}")
}
var varDocs []VarDoc
// loop each field looking for valid comments
for _, f := range complement.Fields.List {
fieldComment := f.Doc.Text()
vd := NewVarDoc(fieldComment)
if vd.Name == "" {
continue // not valid comment
}
vd.Type = typeForExpr(f.Type)
varDocs = append(varDocs, vd)
}
sort.Slice(varDocs, func(i, j int) bool {
return varDocs[i].Name < varDocs[j].Name
})
mdFileLines := []string{
"*This file is automatically generated via ./cmd/gendoc*",
"",
"## Complement-Crypto Configuration",
"Complement is configured exclusively through the use of environment variables. These variables are described below. Additional environment variables can be used, and are outlined at https://github.com/matrix-org/complement/blob/main/ENVIRONMENT.md ",
}
for _, vd := range varDocs {
mdFileLines = append(mdFileLines, fmt.Sprintf("\n#### `%v`", vd.Name))
mdFileLines = append(mdFileLines, vd.Description)
mdFileLines = append(mdFileLines, fmt.Sprintf("- Type: `%v`", vd.Type))
if vd.Default != "" {
mdFileLines = append(mdFileLines, fmt.Sprintf("- Default: %v", vd.Default))
}
}
fmt.Println(strings.Join(mdFileLines, "\n"))
}
85 changes: 85 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package config

import (
"os"
"strings"

"github.com/matrix-org/complement-crypto/internal/api"
)

// The config for running Complement Crypto. This is configured using environment variables. The comments
// in this struct are structured so they can be automatically parsed via gendoc. See /cmd/gendoc.
// There are additional configuration options available: see https://github.com/matrix-org/complement/blob/main/ENVIRONMENT.md
type ComplementCrypto struct {
// Name: COMPLEMENT_CRYPTO_WRITE_CONTAINER_LOGS
// Default: 0
// Description: If 1, writes container logs to ./tests. Useful as a debugging tool.
WriteContainerLogs bool

// Name: COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX
// Default: jj,jr,rj,rr
// Description: The client test matrix to run. Every test is run for each given permutation.
// The default matrix tests all JS/Rust permutations _ignoring federation_.
// Valid values are:
// - `j`: Run a JS SDK client on hs1.
// - `r`: Run a Rust SDK FFI client on hs1.
// - `J`: Run a JS SDK client on hs2.
// - `R`: Run a Rust SDK FFI client on hs2. TODO: needs additional SS proxy / postgres.
// For example, for a simple "Alice and Bob" test:
// - `rj,rr`: Run the test twice. Run 1: Alice=rust, Bob=JS. Run 2: Alice=rust, Bob=rust. All on HS1.
// - `jJ`: Run the test once. Run 1: Alice=JS on HS1, Bob=JS on HS2. Tests federation.
TestClientMatrix [][2]api.ClientType

// Name: COMPLEMENT_CRYPTO_TCPDUMP
// Default: 0
// Description: If 1, automatically attempts to run `tcpdump` when the containers are running. Stops dumping when
// tests complete. This will probably require you to run `go test` with `sudo -E`. The `.pcap` file is written to
// `tests/test.pcap`.
TCPDump bool
}

func NewComplementCryptoConfigFromEnvVars() *ComplementCrypto {
matrix := os.Getenv("COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX")
if matrix == "" {
matrix = "jj,jr,rj,rr"
}
segs := strings.Split(matrix, ",")
var testClientMatrix [][2]api.ClientType
for _, val := range segs { // e.g val == 'rj'
if len(val) != 2 {
panic("COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX bad value: " + val)
}
testCase := [2]api.ClientType{}
for i, ch := range val {
switch ch {
case 'r':
testCase[i] = api.ClientType{
Lang: api.ClientTypeRust,
HS: "hs1",
}
case 'j':
testCase[i] = api.ClientType{
Lang: api.ClientTypeJS,
HS: "hs1",
}
case 'J':
testCase[i] = api.ClientType{
Lang: api.ClientTypeJS,
HS: "hs2",
}
// TODO: case 'R': requires 2x sliding syncs / postgres
default:
panic("COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX bad value: " + val)
}
}
testClientMatrix = append(testClientMatrix, testCase)
}
if len(testClientMatrix) == 0 {
panic("COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX: no tests will run as no matrix values are set")
}
return &ComplementCrypto{
WriteContainerLogs: os.Getenv("COMPLEMENT_CRYPTO_WRITE_CONTAINER_LOGS") == "1",
TCPDump: os.Getenv("COMPLEMENT_CRYPTO_TCPDUMP") == "1",
TestClientMatrix: testClientMatrix,
}
}
50 changes: 8 additions & 42 deletions tests/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,32 @@ package tests

import (
"fmt"
"os"
"strings"
"sync"
"testing"

"github.com/matrix-org/complement"
"github.com/matrix-org/complement-crypto/internal/api"
"github.com/matrix-org/complement-crypto/internal/config"
"github.com/matrix-org/complement-crypto/internal/deploy"
"github.com/matrix-org/complement/client"
"github.com/matrix-org/complement/helpers"
"github.com/matrix-org/complement/must"
)

var (
ssDeployment *deploy.SlidingSyncDeployment
ssMutex *sync.Mutex
testClientMatrix = [][2]api.ClientType{} // set in TestMain
ssDeployment *deploy.SlidingSyncDeployment
ssMutex *sync.Mutex
complementCryptoConfig *config.ComplementCrypto // set in TestMain
)

func TestMain(m *testing.M) {
ccTestClients := os.Getenv("COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX")
if ccTestClients == "" {
ccTestClients = "jj,jr,rj,rr"
}
segs := strings.Split(ccTestClients, ",")
for _, val := range segs { // e.g val == 'rj'
if len(val) != 2 {
panic("COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX bad value: " + val)
}
testCase := [2]api.ClientType{}
for i, ch := range val {
switch ch {
case 'r':
testCase[i] = api.ClientType{
Lang: api.ClientTypeRust,
HS: "hs1",
}
case 'j':
testCase[i] = api.ClientType{
Lang: api.ClientTypeJS,
HS: "hs1",
}
case 'J':
testCase[i] = api.ClientType{
Lang: api.ClientTypeJS,
HS: "hs2",
}
// TODO: case 'R': requires 2x sliding syncs / postgres
default:
panic("COMPLEMENT_CRYPTO_TEST_CLIENT_MATRIX bad value: " + val)
}
}
testClientMatrix = append(testClientMatrix, testCase)
}
complementCryptoConfig = config.NewComplementCryptoConfigFromEnvVars()
ssMutex = &sync.Mutex{}
api.SetupJSLogs("js_sdk.log") // rust sdk logs on its own
complement.TestMainWithCleanup(m, "crypto", func() { // always teardown even if panicking
ssMutex.Lock()
if ssDeployment != nil {
ssDeployment.Teardown(os.Getenv("COMPLEMENT_CRYPTO_WRITE_CONTAINER_LOGS") == "1")
ssDeployment.Teardown(complementCryptoConfig.WriteContainerLogs)
}
ssMutex.Unlock()
api.WriteJSLogs()
Expand All @@ -74,12 +40,12 @@ func Deploy(t *testing.T) *deploy.SlidingSyncDeployment {
if ssDeployment != nil {
return ssDeployment
}
ssDeployment = deploy.RunNewDeployment(t, os.Getenv("COMPLEMENT_CRYPTO_TCPDUMP") == "1")
ssDeployment = deploy.RunNewDeployment(t, complementCryptoConfig.TCPDump)
return ssDeployment
}

func ClientTypeMatrix(t *testing.T, subTest func(tt *testing.T, a, b api.ClientType)) {
for _, tc := range testClientMatrix {
for _, tc := range complementCryptoConfig.TestClientMatrix {
tc := tc
t.Run(fmt.Sprintf("%s|%s", tc[0], tc[1]), func(t *testing.T) {
subTest(t, tc[0], tc[1])
Expand Down

0 comments on commit 0509a2c

Please sign in to comment.