Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tests/antithesis/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ antithesis-build-etcd-image:
--build-arg GO_IMAGE_TAG=$(shell go work edit -json | jq .Go) \
--build-arg REF=$(REF) \
--tag etcd-server:latest \
$(ANTITHESIS_ROOT)/server/
-f ./server/Dockerfile \
$(ANTITHESIS_ROOT)

.PHONY: antithesis-build-etcd-image-release-3.4
antithesis-build-etcd-image-release-3.4: REF=release-3.4
Expand Down
5 changes: 5 additions & 0 deletions tests/antithesis/pkg/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module go.etcd.io/etcd/tests/v3/pkg

go 1.26.4

require github.com/antithesishq/antithesis-sdk-go v0.7.2
2 changes: 2 additions & 0 deletions tests/antithesis/pkg/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/antithesishq/antithesis-sdk-go v0.7.2 h1:oEEedg1Xgi8drRjqB0f9tfjhLoInE0IYZfZ6zAhQUbY=
github.com/antithesishq/antithesis-sdk-go v0.7.2/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
89 changes: 89 additions & 0 deletions tests/antithesis/pkg/rand/rand.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2026 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package rand is a wrapper of https://antithesis.com/docs/generated/sdk/golang/random/
// and provides the same functions as math/rand so it can be a drop-in replacement
//
// The usages of math/rand currently found in etcd (listing only the first occurence of each here)
// to find all, use `for f in $(rg -l 'math/rand"'); do echo $f; grep 'rand\.' $f; done`
//
// 1. out = append(out, rand.Int63n(max-min)+min)
// 2. r := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
// 3. func shuffleEndpoints(r *rand.Rand, eps []string) []string {
// 4. n := rand.Intn(3) + 3
// 5. key := fmt.Sprintf("foo%d", rand.Int())
// 6. membersToChange := rand.Perm(len(clus.Procs))[:numberOfMembersToChange]
// 7. if _, err := rand.Read(v); err != nil {
// 8. multiplier := jitter * (rand.Float64()*2 - 1)
// 9. traceNum := rand.Int31()
// 10. randID = rand.Uint64()
// 11. rand.Shuffle(len(perm), func(i, j int) {
package rand

import (
mrand "math/rand"

antithesis "github.com/antithesishq/antithesis-sdk-go/random"
)

var (
src = antithesis.Source()
r = mrand.New(src)
)

type Rand = mrand.Rand

func New(_ mrand.Source) *mrand.Rand {
return r
}

func NewSource(_ int64) mrand.Source {
return src
}

func Int63n(n int64) int64 {
return r.Int63n(n)
}

func Uint64() uint64 {
return r.Uint64()
}

func Intn(n int) int {
return r.Intn(n)
}

func Int() int {
return r.Int()
}

func Perm(n int) []int {
return r.Perm(n)
}

func Read(p []byte) (n int, err error) {
return r.Read(p)
}

func Float64() float64 {
return r.Float64()
}

func Int31() int32 {
return r.Int31()
}

func Shuffle(n int, swap func(i, j int)) {
r.Shuffle(n, swap)
}
63 changes: 63 additions & 0 deletions tests/antithesis/pkg/rand/v2/randv2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2026 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package rand is a wrapper of https://antithesis.com/docs/generated/sdk/golang/random/
// and provides the same functions as math/rand/v2
//
// The usages of math/rand/v2 currently found in etcd
//
// 1. server/etcdserver/txn/util_bench_test.go
// rng := rand.New(rand.NewPCG(1, 2))
// 2. tests/robustness/validate/operations_test.go
// rand.Shuffle(len(historyCopy), func(i, j int)
// 3. tests/antithesis/test-template/robustness/traffic/main.go
// choice := rand.IntN(len(traffics))
package rand

import (
mrandv1 "math/rand"
mrandv2 "math/rand/v2"

antithesis "github.com/antithesishq/antithesis-sdk-go/random"
)

type sourceV2Func func() uint64

func (f sourceV2Func) Uint64() uint64 {
return f()
}

// antithesis doesn't provide a math/rand/v2 source, so this is
// needed for making a v1 source a v2.
var (
v1Rand = mrandv1.New(antithesis.Source())
v2Source = sourceV2Func(v1Rand.Uint64)
r = mrandv2.New(v2Source)
)

func New(_ mrandv2.Source) *mrandv2.Rand {
return r
}

func NewPCG(_, _ int) mrandv2.Source {
return v2Source
}

func IntN(n int) int {
return r.IntN(n)
}

func Shuffle(n int, swap func(i, j int)) {
r.Shuffle(n, swap)
}
14 changes: 11 additions & 3 deletions tests/antithesis/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,24 @@ ARG REF=main
RUN git clone --depth=1 https://github.com/etcd-io/etcd.git --branch=${REF} /etcd
RUN go env -w GOTOOLCHAIN="go$(cat .go-version)"

# inject assertions in place of gofail
# Replacing math/rand with etcd's antithesis-sdk-go/random wrapper
# See https://antithesis.com/docs/configuration/the_antithesis_environment/?sid=8fce.35&sterm=random&marks=Random#random-devices
WORKDIR /etcd
COPY pkg /etcd/tests/antithesis/pkg
RUN find . -path "./tests/antithesis" -prune -type f -o -name 'go.mod' -type f -execdir go mod edit -replace=math/rand=$(git rev-parse --show-toplevel)/tests/antithesis/pkg/rand \;
RUN find . -path "./tests/antithesis" -prune -type f -o -name 'go.mod' -type f -execdir go mod edit -replace=math/rand/v2=$(git rev-parse --show-toplevel)/tests/antithesis/pkg/rand/v2 \;
RUN find . -name 'go.mod' -type f -execdir go mod tidy \;

## inject assertions in place of gofail
WORKDIR /etcd/server
RUN go install golang.org/x/tools/cmd/goimports@latest
RUN go get github.com/antithesishq/antithesis-sdk-go@v0.4.4
RUN go get github.com/antithesishq/antithesis-sdk-go@v0.7.2
RUN for file in $(grep -rl '// gofail'); do sed -i 's|\/\/ gofail.*var \([[:alnum:]]*\) .*|assert\.Reachable("\1", nil)|' $file; goimports -w $file; done
RUN go mod tidy

# replace verify with antithesis
WORKDIR /etcd
COPY inject/* .
COPY server/inject/* .
RUN if [ "${REF}" = "main" ]; then git apply *.patch; fi
WORKDIR /etcd/client/pkg
RUN go mod tidy
Expand Down