Skip to content

Commit 9887b40

Browse files
committed
stateful db for chaos tests
1 parent 3c5e76a commit 9887b40

File tree

11 files changed

+154
-39
lines changed

11 files changed

+154
-39
lines changed

book/src/framework/first_test.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,6 @@ Summary:
5555
- We defined configuration for `BlockchainNetwork`
5656
- We've used one CTF component in test and checked if it's working
5757

58-
You can learn more about [component design](./components/overview.md) or proceed with another example of [connecting Chainlink node](./connecting_chainlink_node.md)
58+
Now let's connect the [Chainlink](./connecting_chainlink_node.md) node!
5959

6060
Learn more about [anvil](./components/blockchains/anvil.md) component.

book/src/framework/nodeset_environment.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,9 @@ Run it
7272
CTF_CONFIGS=smoke.toml go test -v -run TestNodeSet
7373
```
7474

75-
You'll see something like, use any URL to access CL node
75+
Check the logs to access the UI
7676
```bash
77-
6:14PM INF Chainlink node url URL=http://127.0.0.1:34041
78-
6:14PM INF Chainlink node url URL=http://127.0.0.1:34045
79-
6:14PM INF Chainlink node url URL=http://127.0.0.1:34044
80-
6:14PM INF Chainlink node url URL=http://127.0.0.1:34042
81-
6:14PM INF Chainlink node url URL=http://127.0.0.1:34043
77+
12:41AM INF UI=["http://127.0.0.1:10000","http://127.0.0.1:10001", ...]
8278
```
8379

8480
Use credentials to authorize:

framework/cmd/blockscout.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ func blockscoutUp() error {
1818
if err != nil {
1919
return err
2020
}
21-
framework.L.Info().Msg("Done")
2221
fmt.Println()
2322
framework.L.Info().Msgf("Blockscout is up at: %s", "http://localhost")
2423
return nil
@@ -40,6 +39,5 @@ func blockscoutDown() error {
4039
rm -rf redis-data && \
4140
rm -rf stats-db-data
4241
`, filepath.Join("blockscout", "services")))
43-
framework.L.Info().Msg("Done")
4442
return nil
4543
}

framework/cmd/docker.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ func cleanDockerResources() error {
2424
if err != nil {
2525
return fmt.Errorf("error running clean command: %s", string(output))
2626
}
27-
framework.L.Info().Msgf("Done")
2827
return nil
2928
}
3029

framework/cmd/interactive.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,14 @@ Docker Desktop (https://www.docker.com/products/docker-desktop/)
131131
Options(
132132
huh.NewOption("Anvil", "anvil"),
133133
).
134-
Value(&f.Network), // stores the selected network
134+
Value(&f.Network),
135135

136136
huh.NewSelect[int]().
137137
Title("How many nodes do you need?").
138138
Options(
139139
huh.NewOption("5", 5),
140140
).
141-
Value(&f.Nodes), // stores the selected number of nodes
141+
Value(&f.Nodes),
142142

143143
huh.NewSelect[string]().
144144
Title("Choose Chainlink node version").
@@ -147,11 +147,11 @@ Docker Desktop (https://www.docker.com/products/docker-desktop/)
147147
Value(&f.CLVersion),
148148
huh.NewConfirm().
149149
Title("Do you need to spin up an observability stack?").
150-
Value(&f.Observability), // stores the observability option
150+
Value(&f.Observability),
151151

152152
huh.NewConfirm().
153153
Title("Do you need to spin up a Blockscout stack?").
154-
Value(&f.Blockscout), // stores the Blockscout option
154+
Value(&f.Blockscout),
155155
),
156156
)
157157

@@ -167,13 +167,12 @@ Docker Desktop (https://www.docker.com/products/docker-desktop/)
167167
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
168168
go func() {
169169
<-sigs
170-
fmt.Println("\nReceived Ctrl+C, starting custom cleanup...")
171170
err := cleanup(f)
172171
if err != nil {
173172
panic(err)
174173
}
175174
os.Exit(0)
176175
}()
177-
framework.L.Info().Msg("Press Ctrl+C to remove the stack..")
176+
framework.L.Info().Msg("Services are up! Press Ctrl+C to remove them..")
178177
select {}
179178
}

framework/cmd/observability.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@ func observabilityUp() error {
1717
if err != nil {
1818
return err
1919
}
20-
framework.L.Info().Msg("Done")
2120
fmt.Println()
2221
framework.L.Info().Msgf("Loki: %s", LocalLogsURL)
23-
framework.L.Info().Msgf("All logs: %s", "{job=\"ctf\"}")
24-
framework.L.Info().Msgf("By log level: %s", "{job=\"ctf\", container=~\"node.*\"} |= \"WARN|INFO|DEBUG\"")
2522
framework.L.Info().Msgf("Pyroscope: %s", LocalPyroScopeURL)
2623
return nil
2724
}
@@ -35,6 +32,5 @@ func observabilityDown() error {
3532
if err != nil {
3633
return err
3734
}
38-
framework.L.Info().Msg("Done")
3935
return nil
4036
}

framework/components/postgres/postgres.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@ package postgres
33
import (
44
"context"
55
"fmt"
6+
"github.com/docker/docker/api/types/container"
7+
"github.com/docker/docker/api/types/mount"
68
"github.com/docker/go-connections/nat"
79
"github.com/smartcontractkit/chainlink-testing-framework/framework"
810
"github.com/testcontainers/testcontainers-go"
911
tcwait "github.com/testcontainers/testcontainers-go/wait"
1012
"os"
13+
"path/filepath"
1114
"strings"
1215
"time"
1316
)
1417

1518
const (
16-
User = "chainlink"
17-
Password = "thispasswordislongenough"
18-
Port = "5432"
19-
Database = "chainlink"
20-
Databases = 5
19+
User = "chainlink"
20+
Password = "thispasswordislongenough"
21+
Port = "5432"
22+
ExposedStaticPort = "13000"
23+
Database = "chainlink"
2124
)
2225

2326
type Input struct {
2427
Image string `toml:"image" validate:"required"`
28+
Databases int `toml:"databases"`
2529
PullImage bool `toml:"pull_image"`
2630
Out *Output `toml:"out"`
2731
}
@@ -35,11 +39,10 @@ func NewPostgreSQL(in *Input) (*Output, error) {
3539
ctx := context.Background()
3640

3741
bindPort := fmt.Sprintf("%s/tcp", Port)
38-
3942
containerName := framework.DefaultTCName("postgresql")
4043

4144
var sqlCommands []string
42-
for i := 0; i <= Databases; i++ {
45+
for i := 0; i <= in.Databases; i++ {
4346
sqlCommands = append(sqlCommands, fmt.Sprintf("CREATE DATABASE db_%d;", i))
4447
}
4548
sqlCommands = append(sqlCommands, "ALTER USER chainlink WITH SUPERUSER;")
@@ -82,7 +85,29 @@ func NewPostgreSQL(in *Input) (*Output, error) {
8285
},
8386
WaitingFor: tcwait.ForExec([]string{"psql", "-h", "127.0.0.1",
8487
"-U", User, "-p", Port, "-c", "select", "1", "-d", Database}).
85-
WithStartupTimeout(20 * time.Second),
88+
WithStartupTimeout(20 * time.Second).
89+
WithPollInterval(1 * time.Second),
90+
}
91+
wd, err := os.Getwd()
92+
if err != nil {
93+
return nil, err
94+
}
95+
req.HostConfigModifier = func(h *container.HostConfig) {
96+
h.PortBindings = nat.PortMap{
97+
nat.Port(bindPort): []nat.PortBinding{
98+
{
99+
HostIP: "0.0.0.0",
100+
HostPort: fmt.Sprintf("%s/tcp", ExposedStaticPort),
101+
},
102+
},
103+
}
104+
h.Mounts = []mount.Mount{
105+
{
106+
Type: mount.TypeBind,
107+
Source: filepath.Join(wd, "postgresql_data"),
108+
Target: "/var/lib/postgresql/data",
109+
},
110+
}
86111
}
87112
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
88113
ContainerRequest: req,
@@ -95,10 +120,6 @@ func NewPostgreSQL(in *Input) (*Output, error) {
95120
if err != nil {
96121
return nil, err
97122
}
98-
mp, err := c.MappedPort(ctx, nat.Port(bindPort))
99-
if err != nil {
100-
return nil, err
101-
}
102123
return &Output{
103124
DockerInternalURL: fmt.Sprintf(
104125
"postgresql://%s:%s@%s:%s/%s?sslmode=disable",
@@ -113,7 +134,7 @@ func NewPostgreSQL(in *Input) (*Output, error) {
113134
User,
114135
Password,
115136
host,
116-
mp.Port(),
137+
ExposedStaticPort,
117138
Database,
118139
),
119140
}, nil

framework/components/simple_node_set/node_set.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/clnode"
88
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/postgres"
99
"golang.org/x/sync/errgroup"
10+
"slices"
1011
"strings"
1112
"sync"
1213
)
@@ -16,20 +17,24 @@ const (
1617
DefaultP2PStaticRangeStart = 12000
1718
)
1819

20+
// Input is a node set configuration input
1921
type Input struct {
2022
Nodes int `toml:"nodes" validate:"required"`
2123
HTTPPortRangeStart int `toml:"http_port_range_start"`
2224
P2PPortRangeStart int `toml:"p2p_port_range_start"`
2325
OverrideMode string `toml:"override_mode" validate:"required,oneof=all each"`
24-
NodeSpecs []*clnode.Input `toml:"node_specs"`
26+
NodeSpecs []*clnode.Input `toml:"node_specs" validate:"required"`
2527
Out *Output `toml:"out"`
2628
}
2729

30+
// Output is a node set configuration output, used for caching or external components
2831
type Output struct {
2932
UseCache bool `toml:"use_cache"`
3033
CLNodes []*clnode.Output `toml:"cl_nodes"`
3134
}
3235

36+
// NewSharedDBNodeSet create a new node set with a shared database instance
37+
// all the nodes have their own isolated database
3338
func NewSharedDBNodeSet(in *Input, bcOut *blockchain.Output, fakeUrl string) (*Output, error) {
3439
if in.Out != nil && in.Out.UseCache {
3540
return in.Out, nil
@@ -39,6 +44,7 @@ func NewSharedDBNodeSet(in *Input, bcOut *blockchain.Output, fakeUrl string) (*O
3944
err error
4045
)
4146
defer func() {
47+
printURLs(out)
4248
in.Out = out
4349
}()
4450
if len(in.NodeSpecs) != in.Nodes && in.OverrideMode == "each" {
@@ -56,23 +62,29 @@ func NewSharedDBNodeSet(in *Input, bcOut *blockchain.Output, fakeUrl string) (*O
5662
return nil, err
5763
}
5864
}
59-
printOut(out)
6065
return out, nil
6166
}
6267

63-
func printOut(out *Output) {
64-
for i, n := range out.CLNodes {
65-
framework.L.Info().Str(fmt.Sprintf("Node-%d", i), n.Node.HostURL).Msg("Chainlink node url")
68+
func printURLs(out *Output) {
69+
httpURLs, p2pURLs := make([]string, 0), make([]string, 0)
70+
for _, n := range out.CLNodes {
71+
httpURLs = append(httpURLs, n.Node.HostURL)
72+
p2pURLs = append(p2pURLs, n.Node.HostP2PURL)
6673
}
74+
framework.L.Info().Any("UI", httpURLs).Send()
75+
framework.L.Debug().Any("P2P", p2pURLs).Send()
6776
}
6877

6978
func sharedDBSetup(in *Input, bcOut *blockchain.Output, fakeUrl string, overrideEach bool) (*Output, error) {
79+
in.NodeSpecs[0].DbInput.Databases = in.Nodes
7080
dbOut, err := postgres.NewPostgreSQL(in.NodeSpecs[0].DbInput)
7181
if err != nil {
7282
return nil, err
7383
}
7484
nodeOuts := make([]*clnode.Output, 0)
7585

86+
// to make it easier for chaos testing we use static ports
87+
// there is no need to check them in advance since testcontainers-go returns a nice error
7688
var (
7789
httpPortRangeStart = DefaultHTTPPortStaticRangeStart
7890
p2pPortRangeStart = DefaultP2PStaticRangeStart
@@ -144,8 +156,21 @@ func sharedDBSetup(in *Input, bcOut *blockchain.Output, fakeUrl string, override
144156
if err := eg.Wait(); err != nil {
145157
return nil, err
146158
}
159+
sortNodeOutsByHostPort(nodeOuts)
147160
return &Output{
148161
UseCache: true,
149162
CLNodes: nodeOuts,
150163
}, nil
151164
}
165+
166+
func sortNodeOutsByHostPort(nodes []*clnode.Output) {
167+
slices.SortFunc[[]*clnode.Output, *clnode.Output](nodes, func(a, b *clnode.Output) int {
168+
aa := strings.Split(a.Node.HostURL, ":")
169+
bb := strings.Split(b.Node.HostURL, ":")
170+
if aa[len(aa)-1] < bb[len(bb)-1] {
171+
return -1
172+
} else {
173+
return 1
174+
}
175+
})
176+
}

framework/examples/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ promtail-config.yml
3333
**/blockscout
3434
**/blockscout/*
3535
**/*cache.toml
36+
**/postgresql_data
37+
**/postgresql_data/*

0 commit comments

Comments
 (0)