Skip to content

Commit c8e053e

Browse files
authored
Refactor. (#1)
* Cleaning APIs, attempt 1. Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com> * New API. Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com> * Futures? Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com> * Last Attempt? Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com> * Final result. Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com> * examples. Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com>
1 parent d0843e9 commit c8e053e

38 files changed

Lines changed: 2467 additions & 1944 deletions

.bingo/Variables.mk

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,17 @@ $(GOIMPORTS): $(BINGO_DIR)/goimports.mod
4141
@echo "(re)installing $(GOBIN)/goimports-v0.0.0-20200519204825-e64124511800"
4242
@cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=goimports.mod -o=$(GOBIN)/goimports-v0.0.0-20200519204825-e64124511800 "golang.org/x/tools/cmd/goimports"
4343

44-
GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.26.0
44+
GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.41.1
4545
$(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod
4646
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
47-
@echo "(re)installing $(GOBIN)/golangci-lint-v1.26.0"
48-
@cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.26.0 "github.com/golangci/golangci-lint/cmd/golangci-lint"
47+
@echo "(re)installing $(GOBIN)/golangci-lint-v1.41.1"
48+
@cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=golangci-lint.mod -o=$(GOBIN)/golangci-lint-v1.41.1 "github.com/golangci/golangci-lint/cmd/golangci-lint"
4949

50-
MDOX := $(GOBIN)/mdox-v0.1.1-0.20201227133330-19093fdd9326
50+
MDOX := $(GOBIN)/mdox-v0.2.2-0.20210712170635-f49414cc6b5a
5151
$(MDOX): $(BINGO_DIR)/mdox.mod
5252
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
53-
@echo "(re)installing $(GOBIN)/mdox-v0.1.1-0.20201227133330-19093fdd9326"
54-
@cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=mdox.mod -o=$(GOBIN)/mdox-v0.1.1-0.20201227133330-19093fdd9326 "github.com/bwplotka/mdox"
53+
@echo "(re)installing $(GOBIN)/mdox-v0.2.2-0.20210712170635-f49414cc6b5a"
54+
@cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=mdox.mod -o=$(GOBIN)/mdox-v0.2.2-0.20210712170635-f49414cc6b5a "github.com/bwplotka/mdox"
5555

5656
MISSPELL := $(GOBIN)/misspell-v0.3.4
5757
$(MISSPELL): $(BINGO_DIR)/misspell.mod

.bingo/golangci-lint.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT
22

33
go 1.14
44

5-
require github.com/golangci/golangci-lint v1.26.0 // cmd/golangci-lint
5+
require github.com/golangci/golangci-lint v1.41.1 // cmd/golangci-lint

.bingo/mdox.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT
22

33
go 1.15
44

5-
require github.com/bwplotka/mdox v0.1.1-0.20201227133330-19093fdd9326
5+
require github.com/bwplotka/mdox v0.2.2-0.20210712170635-f49414cc6b5a

.bingo/variables.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ FAILLINT="${GOBIN}/faillint-v1.5.0"
1616

1717
GOIMPORTS="${GOBIN}/goimports-v0.0.0-20200519204825-e64124511800"
1818

19-
GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.26.0"
19+
GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.41.1"
2020

21-
MDOX="${GOBIN}/mdox-v0.1.1-0.20201227133330-19093fdd9326"
21+
MDOX="${GOBIN}/mdox-v0.2.2-0.20210712170635-f49414cc6b5a"
2222

2323
MISSPELL="${GOBIN}/misspell-v0.3.4"
2424

.errcheck_excludes.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(github.com/go-kit/kit/log.Logger).Log
2+
(github.com/efficientgo/e2e.Logger).Log
3+
(*github.com/efficientgo/e2e.SimpleLogger).Log
4+
fmt.Fprintln
5+
fmt.Fprint

.github/workflows/go.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
strategy:
3535
fail-fast: false
3636
matrix:
37-
go: [ '1.14.x', '1.15.x']
37+
go: [ '1.16.x']
3838
platform: [ubuntu-latest, macos-latest]
3939

4040
name: Unit tests on Go ${{ matrix.go }} ${{ matrix.platform }}

.golangci.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# This file contains all available configuration options
2+
# with their default values.
3+
4+
# options for analysis running
5+
run:
6+
# timeout for analysis, e.g. 30s, 5m, default is 1m
7+
deadline: 5m
8+
9+
# exit code when at least one issue was found, default is 1
10+
issues-exit-code: 1
11+
12+
# which dirs to skip: they won't be analyzed;
13+
# can use regexp here: generated.*, regexp is applied on full path;
14+
# default value is empty list, but next dirs are always skipped independently
15+
# from this option's value:
16+
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
17+
skip-dirs: vendor
18+
19+
# output configuration options
20+
output:
21+
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
22+
format: colored-line-number
23+
24+
# print lines of code with issue, default is true
25+
print-issued-lines: true
26+
27+
# print linter name in the end of issue text, default is true
28+
print-linter-name: true
29+
30+
linters:
31+
enable:
32+
# Sorted alphabetically.
33+
- deadcode
34+
- errcheck
35+
- goconst
36+
- godot
37+
- gofmt
38+
- goimports
39+
- gosimple
40+
- govet
41+
- ineffassign
42+
- misspell
43+
- staticcheck
44+
- structcheck
45+
- typecheck
46+
- unparam
47+
- unused
48+
- varcheck
49+
- exportloopref
50+
- promlinter
51+
52+
linters-settings:
53+
errcheck:
54+
exclude: ./.errcheck_excludes.txt
55+
misspell:
56+
locale: US
57+
goconst:
58+
min-occurrences: 5
59+
60+
issues:
61+
exclude-rules:
62+
# We don't check metrics naming in the tests.
63+
- path: _test\.go
64+
linters:
65+
- promlinter

Makefile

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,17 @@ deps: ## Cleans up deps for all modules
5454
cd $${dir} && go mod tidy; \
5555
done
5656

57+
define MDOX_VALIDATORS
58+
validators:
59+
- regex: '^(http://.*|https://.*)'
60+
type: ignore
61+
endef
62+
63+
export MDOX_VALIDATORS
5764
.PHONY: docs
5865
docs: $(MDOX) ## Generates config snippets and doc formatting.
5966
@echo ">> generating docs $(PATH)"
60-
@$(MDOX) fmt -l *.md
67+
@$(MDOX) fmt -l --links.validate.config="$$MDOX_VALIDATORS" *.md
6168

6269
.PHONY: format
6370
format: ## Formats Go code.
@@ -72,6 +79,11 @@ test: ## Runs all Go unit tests.
7279
cd $${dir} && go test -v -race ./...; \
7380
done
7481

82+
.PHONY: run-example
83+
run-example: ## Runs our standalone Thanos example using e2e.
84+
@echo ">> running example"
85+
cd examples/thanos && go run .
86+
7587
.PHONY: check-git
7688
check-git:
7789
ifneq ($(GIT),)
@@ -89,16 +101,16 @@ lint: ## Runs various static analysis against our code.
89101
lint: $(FAILLINT) $(GOLANGCI_LINT) $(MISSPELL) $(COPYRIGHT) build format docs check-git deps
90102
$(call require_clean_work_tree,"detected not clean master before running lint - run make lint and commit changes.")
91103
@echo ">> verifying imported "
92-
for dir in $(MODULES) ; do \
93-
cd $${dir} && $(FAILLINT) -paths "fmt.{Print,PrintfPrintln,Sprint}" -ignore-tests ./... && \
94-
$(FAILLINT) -paths "github.com/stretchr/testify=github.com/efficientgo/tools/core/pkg/testutil" ./...; \
104+
@for dir in $(MODULES) ; do \
105+
cd $${dir} && $(FAILLINT) -paths "fmt.{Print,PrintfPrintln}" -ignore-tests ./... && \
106+
$(FAILLINT) -paths "github.com/stretchr/testify=github.com/efficientgo/tools/core/pkg/testutil,fmt.{Errorf}=github.com/pkg/errors" ./...; \
95107
done
96108
@echo ">> examining all of the Go files"
97-
for dir in $(MODULES) ; do \
109+
@for dir in $(MODULES) ; do \
98110
cd $${dir} && go vet -stdmethods=false ./...; \
99111
done
100112
@echo ">> linting all of the Go files GOGC=${GOGC}"
101-
for dir in $(MODULES) ; do \
113+
@for dir in $(MODULES) ; do \
102114
cd $${dir} && $(GOLANGCI_LINT) run; \
103115
done
104116
@echo ">> detecting misspells"

README.md

Lines changed: 118 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,126 @@
22

33
[![golang docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/e2e)
44

5-
Robust framework for running complex workload scenarios in isolation, using Go; for integration, e2e tests, benchmarks and more!
5+
Go Module providing robust framework for running complex workload scenarios in isolation, using Go and Docker. For integration, e2e tests, benchmarks and more! 💪
66

7-
## What is it?
7+
## What are the goals?
88

9-
`e2e` is a Go module which implements a fully featured e2e suite allowing utilizing `go test` for setting hermetic up complex microservice testing scenarios using `docker`.
9+
* Ability to schedule isolated processes programmatically from single process on single machine.
10+
* Focus on cluster workloads, cloud native services and microservices.
11+
* Developer scenarios in mind e.g preserving scenario readability, Go unit test integration.
12+
* Metric monitoring as the first citizen. Assert on Prometheus metric values during test scenarios or check overall performance characteristics.
1013

11-
```go mdox-gen-exec="sh -c 'tail -n +6 e2e/doc.go'"
12-
// This module is a fully featured e2e suite allowing utilizing `go test` for setting hermetic up complex microservice integration testing scenarios using docker.
13-
// Example usages:
14-
// * https://github.com/cortexproject/cortex/tree/master/integration
15-
// * https://github.com/thanos-io/thanos/tree/master/test/e2e
16-
//
17-
// Check github.com/efficientgo/tools/e2e/db for common DBs services you can run out of the box.
18-
```
14+
## Usage Models
1915

20-
Credits:
16+
There are three main use cases envisioned for this Go module:
2117

22-
* [Cortex Team](https://github.com/cortexproject/cortex/tree/f639b1855c9f0c9564113709a6bce2996d151ec7/integration)
23-
* Initial Authors: [@pracucci](https://github.com/pracucci), [@bwplotka](https://github.com/bwplotka)
18+
* *Unit test use* ([see example](examples/thanos/unittest_test.go)). Use `e2e` in unit tests to quickly run complex test scenarios involving many container services. This was the main reason we created this module. You can check usage of it in [Cortex](https://github.com/cortexproject/cortex/tree/main/integration) and [Thanos](https://github.com/thanos-io/thanos/tree/main/test/e2e) projects.
19+
* *Standalone use* ([see example](examples/thanos/standalone.go)). Use `e2e` to run setups in interactive mode where you spin up workloads as you want *programmatically* and poke with it on your own using your browser or other tools. No longer need to deploy full Kubernetes or external machines.
20+
* *Benchmark use* ([see example](examples/thanos/benchmark_test.go)). Use `e2e` in local Go benchmarks when your code depends on external services with ease.
21+
22+
### Getting Started
23+
24+
Let's go through an example leveraging `go test` flow:
25+
26+
1. Implement the workload by embedding`e2e.Runnable` or `*e2e.InstrumentedRunnable`. Or you can use existing ones in [e2edb](db/) package. For example implementing Thanos Querier with our desired configuration could look like this:
27+
28+
```go mdox-exec="sed -n '47,64p' examples/thanos/standalone.go"
29+
func newThanosSidecar(env e2e.Environment, name string, prom e2e.Linkable) *e2e.InstrumentedRunnable {
30+
ports := map[string]int{
31+
"http": 9090,
32+
"grpc": 9091,
33+
}
34+
return e2e.NewInstrumentedRunnable(env, name, ports, "http", e2e.StartOptions{
35+
Image: "quay.io/thanos/thanos:v0.21.1",
36+
Command: e2e.NewCommand("sidecar", e2e.BuildArgs(map[string]string{
37+
"--debug.name": name,
38+
"--grpc-address": fmt.Sprintf(":%d", ports["grpc"]),
39+
"--http-address": fmt.Sprintf(":%d", ports["http"]),
40+
"--prometheus.url": "http://" + prom.InternalEndpoint(e2edb.AccessPortName),
41+
"--log.level": "info",
42+
})...),
43+
Readiness: e2e.NewHTTPReadinessProbe("http", "/-/ready", 200, 200),
44+
User: strconv.Itoa(os.Getuid()),
45+
})
46+
}
47+
```
48+
49+
2. Implement test. Start by creating environment. Currently `e2e` supports Docker environment only. Use unique name for all your tests. It's recommended to keep it stable so resources are consistently cleaned.
50+
51+
```go mdox-exec="sed -n '22,26p' examples/thanos/unittest_test.go"
52+
// Start isolated environment with given ref.
53+
e, err := e2e.NewDockerEnvironment("e2e_example")
54+
testutil.Ok(t, err)
55+
// Make sure resources (e.g docker containers, network, dir) are cleaned.
56+
t.Cleanup(e.Close)
57+
```
58+
59+
3. Program your scenario as you want. You can start, wait for their readiness, stop, check their metrics and use their network endpoints from both unit test (`Endpoint`) as well as within each workload (`InternalEndpoint`). You can also access workload directory. There is a shared directory across all workloads. Check `Dir` and `InternalDir` runnable methods.
60+
61+
```go mdox-exec="sed -n '28,87p' examples/thanos/unittest_test.go"
62+
// Create structs for Prometheus containers scraping itself.
63+
p1, err := e2edb.NewPrometheus(e, "prometheus-1")
64+
testutil.Ok(t, err)
65+
s1 := newThanosSidecar(e, "sidecar-1", p1)
66+
67+
p2, err := e2edb.NewPrometheus(e, "prometheus-2")
68+
testutil.Ok(t, err)
69+
s2 := newThanosSidecar(e, "sidecar-2", p2)
70+
71+
// Create Thanos Query container. We can point the peer network addresses of both Prometheus instance
72+
// using InternalEndpoint methods, even before they started.
73+
t1 := newThanosQuerier(e, "query-1", s1.InternalEndpoint("grpc"), s2.InternalEndpoint("grpc"))
74+
75+
// Start them.
76+
testutil.Ok(t, e2e.StartAndWaitReady(p1, s1, p2, s2, t1))
77+
78+
// To ensure query should have access we can check its Prometheus metric using WaitSumMetrics method. Since the metric we are looking for
79+
// only appears after init, we add option to wait for it.
80+
testutil.Ok(t, t1.WaitSumMetricsWithOptions(e2e.Equals(2), []string{"thanos_store_nodes_grpc_connections"}, e2e.WaitMissingMetrics()))
81+
82+
// To ensure Prometheus scraped already something ensure number of scrapes.
83+
testutil.Ok(t, p1.WaitSumMetrics(e2e.Greater(50), "prometheus_tsdb_head_samples_appended_total"))
84+
testutil.Ok(t, p2.WaitSumMetrics(e2e.Greater(50), "prometheus_tsdb_head_samples_appended_total"))
85+
86+
// We can now query Thanos Querier directly from here, using it's host address thanks to Endpoint method.
87+
a, err := api.NewClient(api.Config{Address: "http://" + t1.Endpoint("http")})
88+
testutil.Ok(t, err)
89+
90+
{
91+
now := model.Now()
92+
v, w, err := v1.NewAPI(a).Query(context.Background(), "up{}", now.Time())
93+
testutil.Ok(t, err)
94+
testutil.Equals(t, 0, len(w))
95+
testutil.Equals(
96+
t,
97+
fmt.Sprintf(`up{instance="%v", job="myself", prometheus="prometheus-1"} => 1 @[%v]
98+
up{instance="%v", job="myself", prometheus="prometheus-2"} => 1 @[%v]`, p1.InternalEndpoint(e2edb.AccessPortName), now, p2.InternalEndpoint(e2edb.AccessPortName), now),
99+
v.String(),
100+
)
101+
}
102+
103+
// Stop first Prometheus and sidecar.
104+
testutil.Ok(t, s1.Stop())
105+
testutil.Ok(t, p1.Stop())
106+
107+
// Wait a bit until Thanos drops connection to stopped Prometheus.
108+
testutil.Ok(t, t1.WaitSumMetricsWithOptions(e2e.Equals(1), []string{"thanos_store_nodes_grpc_connections"}, e2e.WaitMissingMetrics()))
109+
110+
{
111+
now := model.Now()
112+
v, w, err := v1.NewAPI(a).Query(context.Background(), "up{}", now.Time())
113+
testutil.Ok(t, err)
114+
testutil.Equals(t, 0, len(w))
115+
testutil.Equals(
116+
t,
117+
fmt.Sprintf(`up{instance="%v", job="myself", prometheus="prometheus-2"} => 1 @[%v]`, p2.InternalEndpoint(e2edb.AccessPortName), now),
118+
v.String(),
119+
)
120+
}
121+
}
122+
```
123+
124+
## Credits
125+
126+
* Initial Authors: [@pracucci](https://github.com/pracucci), [@bwplotka](https://github.com/bwplotka), [@pstibrany](https://github.com/pstibrany)
127+
* [Cortex Team](https://github.com/cortexproject/cortex/tree/f639b1855c9f0c9564113709a6bce2996d151ec7/integration) hosting previous form of this module initially.

cli.go

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,6 @@
33

44
package e2e
55

6-
import (
7-
"context"
8-
"os/exec"
9-
"time"
10-
)
11-
12-
func RunCommandAndGetOutput(name string, args ...string) ([]byte, error) {
13-
cmd := exec.Command(name, args...)
14-
return cmd.CombinedOutput()
15-
}
16-
17-
func RunCommandWithTimeoutAndGetOutput(timeout time.Duration, name string, args ...string) ([]byte, error) {
18-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
19-
defer cancel()
20-
21-
cmd := exec.CommandContext(ctx, name, args...)
22-
return cmd.CombinedOutput()
23-
}
24-
256
func EmptyFlags() map[string]string {
267
return map[string]string{}
278
}
@@ -34,7 +15,6 @@ func MergeFlags(inputs ...map[string]string) map[string]string {
3415
delete(output, k)
3516
}
3617
}
37-
3818
return output
3919
}
4020

@@ -46,7 +26,6 @@ func MergeFlagsWithoutRemovingEmpty(inputs ...map[string]string) map[string]stri
4626
output[name] = value
4727
}
4828
}
49-
5029
return output
5130
}
5231

@@ -60,6 +39,5 @@ func BuildArgs(flags map[string]string) []string {
6039
args = append(args, name)
6140
}
6241
}
63-
6442
return args
6543
}

0 commit comments

Comments
 (0)