Skip to content

Commit

Permalink
testutil/compose: add run command
Browse files Browse the repository at this point in the history
  • Loading branch information
corverroos committed May 20, 2022
1 parent 43ae0fa commit c08ecbc
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 7 deletions.
24 changes: 22 additions & 2 deletions testutil/compose/compose/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,33 @@ func newRootCmd() *cobra.Command {

root.AddCommand(newDefineCmd())
root.AddCommand(newLockCmd())
root.AddCommand(newRunCmd())

return root
}

func newRunCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "run",
Short: "Create a docker-compose.yml from charon-compose.yml to run the cluster.",
}

dir := addDirFlag(cmd)

cmd.RunE = func(cmd *cobra.Command, _ []string) error {
return compose.Run(cmd.Context(), *dir)
}

return cmd
}

func newLockCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "lock",
Short: "Create a docker-compose.yml from charon-compose.yml for generating keys and a cluster lock file.",
}

dir := cmd.Flags().String("compose-dir", ".", "Directory to use for compose artifacts")
dir := addDirFlag(cmd)

cmd.RunE = func(cmd *cobra.Command, _ []string) error {
return compose.Lock(cmd.Context(), *dir)
Expand All @@ -67,7 +83,7 @@ func newDefineCmd() *cobra.Command {
Short: "Create a charon-compose.yml definition; including both keygen and running definitions",
}

dir := cmd.Flags().String("compose-dir", ".", "Directory to use for compose artifacts")
dir := addDirFlag(cmd)
clean := cmd.Flags().Bool("clean", true, "Clean compose dir before defining a new cluster")
seed := cmd.Flags().Int("seed", int(time.Now().UnixNano()), "Randomness seed")

Expand All @@ -77,3 +93,7 @@ func newDefineCmd() *cobra.Command {

return cmd
}

func addDirFlag(cmd *cobra.Command) *string {
return cmd.Flags().String("compose-dir", ".", "Directory to use for compose artifacts")
}
3 changes: 2 additions & 1 deletion testutil/compose/docker-compose.template
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ services:
volumes:
- .:/compose
{{end}}

{{end}}
{{if .Monitoring}}
prometheus:
image: prom/prometheus:latest
ports:
Expand Down
12 changes: 9 additions & 3 deletions testutil/compose/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/obolnetwork/charon/app/log"
)

// Lock creates a docker-compose.yml from a charon-compose.yml for generating keys and a cluster lock file.
func Lock(ctx context.Context, dir string) error {
ctx = log.WithTopic(ctx, "lock")

Expand Down Expand Up @@ -61,18 +62,23 @@ func Lock(ctx context.Context, dir string) error {
return writeDockerCompose(dir, data)
}

//nolint:deadcode // Busy implementing.
func newNodeEnvs(mockValidator bool) []kv {
// newNodeEnvs returns the default node environment variable to run a charon docker container.

func newNodeEnvs(index int, validatorMock, beaconMock bool) []kv {
return []kv{
{"data_dir", fmt.Sprintf("/compose/node%d", index)},
{"jaeger_service", fmt.Sprintf("node%d", index)},
{"jaeger_address", "jaeger:6831"},
{"definition_file", "/compose/cluster-definition.json"},
{"lock_file", "/compose/cluster-lock.json"},
{"monitoring_address", "0.0.0.0:16001"},
{"validator_api_address", "0.0.0.0:16002"},
{"p2p_external_hostname", fmt.Sprintf("node%d", index)},
{"p2p_tcp_address", "0.0.0.0:16003"},
{"p2p_udp_address", "0.0.0.0:16004"},
{"p2p_bootnodes", "http://bootnode:16000/enr"},
{"simnet_validator_mock", fmt.Sprint(mockValidator)},
{"simnet_validator_mock", fmt.Sprintf(`"%v"`, validatorMock)},
{"simnet_beacon_mock", fmt.Sprintf(`"%v"`, beaconMock)},
{"log_level", "info"},
}
}
Expand Down
50 changes: 50 additions & 0 deletions testutil/compose/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright © 2022 Obol Labs Inc.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.

package compose

import (
"context"

"github.com/obolnetwork/charon/app/log"
)

func Run(ctx context.Context, dir string) error {
ctx = log.WithTopic(ctx, "run")

conf, err := loadConfig(dir)
if err != nil {
return err
}

var nodes []node
for i := 0; i < len(conf.Def.Operators); i++ {
n := node{EnvVars: newNodeEnvs(i, true, true)}
nodes = append(nodes, n)
}

data := tmplData{
ComposeDir: dir,
CharonImageTag: conf.ImageTag,
CharonEntrypoint: containerBinary,
CharonCommand: cmdRun,
Nodes: nodes,
}

log.Info(ctx, "Created docker-compose.yml")
log.Info(ctx, "Run the cluster with: docker-compose up")

return writeDockerCompose(dir, data)
}
47 changes: 47 additions & 0 deletions testutil/compose/run_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright © 2022 Obol Labs Inc.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.

package compose

import (
"bytes"
"context"
"os"
"path"
"testing"

"github.com/stretchr/testify/require"

"github.com/obolnetwork/charon/testutil"
)

func TestRunCompose(t *testing.T) {
dir, err := os.MkdirTemp("", "")
require.NoError(t, err)

conf, _ := newDefaultConfig(1)

err = writeConfig(dir, conf)
require.NoError(t, err)

err = Run(context.Background(), dir)
require.NoError(t, err)

compose, err := os.ReadFile(path.Join(dir, "docker-compose.yml"))
require.NoError(t, err)
compose = bytes.ReplaceAll(compose, []byte(dir), []byte("testdir"))

testutil.RequireGoldenBytes(t, compose)
}
3 changes: 2 additions & 1 deletion testutil/compose/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ type tmplData struct {
Nodes []node
VCs []vc

NodeOnly bool
NodeOnly bool
Monitoring bool
}

// vc represents a validator client service in a docker-compose.yml.
Expand Down
1 change: 1 addition & 0 deletions testutil/compose/testdata/TestLockCompose.golden
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ services:
CHARON_CLUSTER_DIR: /compose



networks:
compose:
101 changes: 101 additions & 0 deletions testutil/compose/testdata/TestRunCompose.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
version: "3.8"

x-node-base: &node-base
image: ghcr.io/obolnetwork/charon:latest
entrypoint: /usr/local/bin/charon
command: run
networks: [compose]
volumes: [testdir:/compose]
depends_on: [bootnode]

services:

node0:
<<: *node-base
environment:
CHARON_DATA_DIR: /compose/node0
CHARON_JAEGER_SERVICE: node0
CHARON_JAEGER_ADDRESS: jaeger:6831
CHARON_DEFINITION_FILE: /compose/cluster-definition.json
CHARON_LOCK_FILE: /compose/cluster-lock.json
CHARON_MONITORING_ADDRESS: 0.0.0.0:16001
CHARON_VALIDATOR_API_ADDRESS: 0.0.0.0:16002
CHARON_P2P_EXTERNAL_HOSTNAME: node0
CHARON_P2P_TCP_ADDRESS: 0.0.0.0:16003
CHARON_P2P_UDP_ADDRESS: 0.0.0.0:16004
CHARON_P2P_BOOTNODES: http://bootnode:16000/enr
CHARON_SIMNET_VALIDATOR_MOCK: "true"
CHARON_SIMNET_BEACON_MOCK: "true"
CHARON_LOG_LEVEL: info


node1:
<<: *node-base
environment:
CHARON_DATA_DIR: /compose/node1
CHARON_JAEGER_SERVICE: node1
CHARON_JAEGER_ADDRESS: jaeger:6831
CHARON_DEFINITION_FILE: /compose/cluster-definition.json
CHARON_LOCK_FILE: /compose/cluster-lock.json
CHARON_MONITORING_ADDRESS: 0.0.0.0:16001
CHARON_VALIDATOR_API_ADDRESS: 0.0.0.0:16002
CHARON_P2P_EXTERNAL_HOSTNAME: node1
CHARON_P2P_TCP_ADDRESS: 0.0.0.0:16003
CHARON_P2P_UDP_ADDRESS: 0.0.0.0:16004
CHARON_P2P_BOOTNODES: http://bootnode:16000/enr
CHARON_SIMNET_VALIDATOR_MOCK: "true"
CHARON_SIMNET_BEACON_MOCK: "true"
CHARON_LOG_LEVEL: info


node2:
<<: *node-base
environment:
CHARON_DATA_DIR: /compose/node2
CHARON_JAEGER_SERVICE: node2
CHARON_JAEGER_ADDRESS: jaeger:6831
CHARON_DEFINITION_FILE: /compose/cluster-definition.json
CHARON_LOCK_FILE: /compose/cluster-lock.json
CHARON_MONITORING_ADDRESS: 0.0.0.0:16001
CHARON_VALIDATOR_API_ADDRESS: 0.0.0.0:16002
CHARON_P2P_EXTERNAL_HOSTNAME: node2
CHARON_P2P_TCP_ADDRESS: 0.0.0.0:16003
CHARON_P2P_UDP_ADDRESS: 0.0.0.0:16004
CHARON_P2P_BOOTNODES: http://bootnode:16000/enr
CHARON_SIMNET_VALIDATOR_MOCK: "true"
CHARON_SIMNET_BEACON_MOCK: "true"
CHARON_LOG_LEVEL: info


node3:
<<: *node-base
environment:
CHARON_DATA_DIR: /compose/node3
CHARON_JAEGER_SERVICE: node3
CHARON_JAEGER_ADDRESS: jaeger:6831
CHARON_DEFINITION_FILE: /compose/cluster-definition.json
CHARON_LOCK_FILE: /compose/cluster-lock.json
CHARON_MONITORING_ADDRESS: 0.0.0.0:16001
CHARON_VALIDATOR_API_ADDRESS: 0.0.0.0:16002
CHARON_P2P_EXTERNAL_HOSTNAME: node3
CHARON_P2P_TCP_ADDRESS: 0.0.0.0:16003
CHARON_P2P_UDP_ADDRESS: 0.0.0.0:16004
CHARON_P2P_BOOTNODES: http://bootnode:16000/enr
CHARON_SIMNET_VALIDATOR_MOCK: "true"
CHARON_SIMNET_BEACON_MOCK: "true"
CHARON_LOG_LEVEL: info


bootnode:
<<: *node-base
command: bootnode
depends_on: []
environment:
CHARON_BOOTNODE_HTTP_ADDRESS: 0.0.0.0:16000
CHARON_DATA_DIR: /compose/bootnode
CHARON_P2P_BOOTNODES: ""
CHARON_P2P_EXTERNAL_HOSTNAME: bootnode


networks:
compose:

0 comments on commit c08ecbc

Please sign in to comment.