implement a basic infra provider with a simple setup command

This commit is contained in:
William Banfield
2022-10-18 16:45:56 -04:00
parent e4fb662c8d
commit 38d1b2f873
4 changed files with 137 additions and 79 deletions

View File

@@ -0,0 +1,100 @@
package docker
import (
"bytes"
"os"
"path/filepath"
"strconv"
"text/template"
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
"github.com/tendermint/tendermint/test/e2e/pkg/infra"
)
var _ infra.Provider = &Provider{}
// Provider implements a docker-compose backed infrastructure provider.
type Provider struct {
Testnet *e2e.Testnet
}
// Setup generates the docker-compose file and write it to disk, erroring if
// any of these operations fail.
func (p *Provider) Setup() error {
compose, err := dockerComposeBytes(p.Testnet)
if err != nil {
return err
}
// nolint: gosec
// G306: Expect WriteFile permissions to be 0600 or less
err = os.WriteFile(filepath.Join(p.Testnet.Dir, "docker-compose.yml"), compose, 0644)
if err != nil {
return err
}
return nil
}
// dockerComposeBytes generates a Docker Compose config file for a testnet and returns the
// file as bytes to be written out to disk.
func dockerComposeBytes(testnet *e2e.Testnet) ([]byte, error) {
// Must use version 2 Docker Compose format, to support IPv6.
tmpl, err := template.New("docker-compose").Funcs(template.FuncMap{
"misbehaviorsToString": func(misbehaviors map[int64]string) string {
str := ""
for height, misbehavior := range misbehaviors {
// after the first behavior set, a comma must be prepended
if str != "" {
str += ","
}
heightString := strconv.Itoa(int(height))
str += misbehavior + "," + heightString
}
return str
},
}).Parse(`version: '2.4'
networks:
{{ .Name }}:
labels:
e2e: true
driver: bridge
{{- if .IPv6 }}
enable_ipv6: true
{{- end }}
ipam:
driver: default
config:
- subnet: {{ .IP }}
services:
{{- range .Nodes }}
{{ .Name }}:
labels:
e2e: true
container_name: {{ .Name }}
image: tendermint/e2e-node
{{- if eq .ABCIProtocol "builtin" }}
entrypoint: /usr/bin/entrypoint-builtin
{{- end }}
init: true
ports:
- 26656
- {{ if .ProxyPort }}{{ .ProxyPort }}:{{ end }}26657
- 6060
volumes:
- ./{{ .Name }}:/tendermint
networks:
{{ $.Name }}:
ipv{{ if $.IPv6 }}6{{ else }}4{{ end}}_address: {{ .IP }}
{{end}}`)
if err != nil {
return nil, err
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, testnet)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@@ -0,0 +1,20 @@
package infra
// Provider defines an API for manipulating the infrastructure of a
// specific set of testnet infrastructure.
type Provider interface {
// Setup generates any necessary configuration for the infrastructure
// provider during testnet setup.
Setup() error
}
// NoopProvider implements the provider interface by performing noops for every
// interface method. This may be useful if the infrastructure is managed by a
// separate process.
type NoopProvider struct {
}
func (_ NoopProvider) Setup() error { return nil }
var _ Provider = NoopProvider{}

View File

@@ -12,6 +12,8 @@ import (
"github.com/tendermint/tendermint/libs/log"
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
"github.com/tendermint/tendermint/test/e2e/pkg/infra"
"github.com/tendermint/tendermint/test/e2e/pkg/infra/docker"
)
const randomSeed = 2308084734268
@@ -27,6 +29,7 @@ type CLI struct {
root *cobra.Command
testnet *e2e.Testnet
preserve bool
infp infra.Provider
}
// NewCLI sets up the CLI.
@@ -47,13 +50,13 @@ func NewCLI() *CLI {
return err
}
t, err := cmd.Flags().GetString("infrastructure-type")
inft, err := cmd.Flags().GetString("infrastructure-type")
if err != nil {
return err
}
var ifd e2e.InfrastructureData
switch t {
switch inft {
case "docker":
var err error
ifd, err = e2e.NewDockerInfrastructureData(m)
@@ -73,7 +76,7 @@ func NewCLI() *CLI {
return err
}
default:
return fmt.Errorf("unknown infrastructure type '%s'", t)
return fmt.Errorf("unknown infrastructure type '%s'", inft)
}
testnet, err := e2e.LoadTestnet(file, ifd)
@@ -82,13 +85,17 @@ func NewCLI() *CLI {
}
cli.testnet = testnet
cli.infp = &infra.NoopProvider{}
if inft == "docker" {
cli.infp = &docker.Provider{Testnet: testnet}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := Cleanup(cli.testnet); err != nil {
return err
}
if err := Setup(cli.testnet); err != nil {
if err := Setup(cli.testnet, cli.infp); err != nil {
return err
}
@@ -164,7 +171,7 @@ func NewCLI() *CLI {
Use: "setup",
Short: "Generates the testnet directory and configuration",
RunE: func(cmd *cobra.Command, args []string) error {
return Setup(cli.testnet)
return Setup(cli.testnet, cli.infp)
},
})
@@ -174,7 +181,7 @@ func NewCLI() *CLI {
RunE: func(cmd *cobra.Command, args []string) error {
_, err := os.Stat(cli.testnet.Dir)
if os.IsNotExist(err) {
err = Setup(cli.testnet)
err = Setup(cli.testnet, cli.infp)
}
if err != nil {
return err
@@ -297,7 +304,7 @@ Does not run any perbutations.
if err := Cleanup(cli.testnet); err != nil {
return err
}
if err := Setup(cli.testnet); err != nil {
if err := Setup(cli.testnet, cli.infp); err != nil {
return err
}

View File

@@ -10,9 +10,7 @@ import (
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"text/template"
"time"
"github.com/BurntSushi/toml"
@@ -23,6 +21,7 @@ import (
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
"github.com/tendermint/tendermint/test/e2e/pkg/infra"
"github.com/tendermint/tendermint/types"
)
@@ -39,7 +38,7 @@ const (
)
// Setup sets up the testnet configuration.
func Setup(testnet *e2e.Testnet) error {
func Setup(testnet *e2e.Testnet, infp infra.Provider) error {
logger.Info("setup", "msg", log.NewLazySprintf("Generating testnet files in %q", testnet.Dir))
err := os.MkdirAll(testnet.Dir, os.ModePerm)
@@ -47,11 +46,7 @@ func Setup(testnet *e2e.Testnet) error {
return err
}
compose, err := MakeDockerCompose(testnet)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(testnet.Dir, "docker-compose.yml"), compose, 0o644) //nolint:gosec
err = infp.Setup()
if err != nil {
return err
}
@@ -126,70 +121,6 @@ func Setup(testnet *e2e.Testnet) error {
return nil
}
// MakeDockerCompose generates a Docker Compose config for a testnet.
func MakeDockerCompose(testnet *e2e.Testnet) ([]byte, error) {
// Must use version 2 Docker Compose format, to support IPv6.
tmpl, err := template.New("docker-compose").Funcs(template.FuncMap{
"misbehaviorsToString": func(misbehaviors map[int64]string) string {
str := ""
for height, misbehavior := range misbehaviors {
// after the first behavior set, a comma must be prepended
if str != "" {
str += ","
}
heightString := strconv.Itoa(int(height))
str += misbehavior + "," + heightString
}
return str
},
}).Parse(`version: '2.4'
networks:
{{ .Name }}:
labels:
e2e: true
driver: bridge
{{- if .IPv6 }}
enable_ipv6: true
{{- end }}
ipam:
driver: default
config:
- subnet: {{ .IP }}
services:
{{- range .Nodes }}
{{ .Name }}:
labels:
e2e: true
container_name: {{ .Name }}
image: tendermint/e2e-node
{{- if eq .ABCIProtocol "builtin" }}
entrypoint: /usr/bin/entrypoint-builtin
{{- end }}
init: true
ports:
- 26656
- {{ if .ProxyPort }}{{ .ProxyPort }}:{{ end }}26657
- 6060
volumes:
- ./{{ .Name }}:/tendermint
networks:
{{ $.Name }}:
ipv{{ if $.IPv6 }}6{{ else }}4{{ end}}_address: {{ .IP }}
{{end}}`)
if err != nil {
return nil, err
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, testnet)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// MakeGenesis generates a genesis document.
func MakeGenesis(testnet *e2e.Testnet) (types.GenesisDoc, error) {
genesis := types.GenesisDoc{