From 38d1b2f873b4fc938320abb07fd5a3e62e0560ff Mon Sep 17 00:00:00 2001 From: William Banfield Date: Tue, 18 Oct 2022 16:45:56 -0400 Subject: [PATCH] implement a basic infra provider with a simple setup command --- test/e2e/pkg/infra/docker/docker.go | 100 ++++++++++++++++++++++++++++ test/e2e/pkg/infra/provider.go | 20 ++++++ test/e2e/runner/main.go | 21 ++++-- test/e2e/runner/setup.go | 75 +-------------------- 4 files changed, 137 insertions(+), 79 deletions(-) create mode 100644 test/e2e/pkg/infra/docker/docker.go create mode 100644 test/e2e/pkg/infra/provider.go diff --git a/test/e2e/pkg/infra/docker/docker.go b/test/e2e/pkg/infra/docker/docker.go new file mode 100644 index 000000000..4d52a3d37 --- /dev/null +++ b/test/e2e/pkg/infra/docker/docker.go @@ -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 +} diff --git a/test/e2e/pkg/infra/provider.go b/test/e2e/pkg/infra/provider.go new file mode 100644 index 000000000..ebf053ccd --- /dev/null +++ b/test/e2e/pkg/infra/provider.go @@ -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{} diff --git a/test/e2e/runner/main.go b/test/e2e/runner/main.go index c2a7cbc41..4f56a9e24 100644 --- a/test/e2e/runner/main.go +++ b/test/e2e/runner/main.go @@ -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 } diff --git a/test/e2e/runner/setup.go b/test/e2e/runner/setup.go index 202f3f79e..e6aec065b 100644 --- a/test/e2e/runner/setup.go +++ b/test/e2e/runner/setup.go @@ -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{