Skip to content

Commit 8ee90f8

Browse files
committed
Add mirror test implementation
- Implement mirror test structure - Add test/common functionality - Create ctl_v3_make_mirror_test Signed-off-by: Ronald Ngounou <ronald.ngounou@yahoo.com>
1 parent 1b2ed5a commit 8ee90f8

File tree

10 files changed

+290
-2
lines changed

10 files changed

+290
-2
lines changed

tests/common/e2e_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,37 @@ func WithUnixClient() config.ClusterOption {
120120
}
121121

122122
func WithTCPClient() config.ClusterOption {
123-
return e2e.WithTCPClient()
123+
return func(c *config.ClusterConfig) {
124+
ctx := ensureE2EClusterContext(c)
125+
ctx.UseUnix = false
126+
c.ClusterContext = ctx
127+
}
128+
}
129+
130+
func WithBasePort(port int) config.ClusterOption {
131+
return func(c *config.ClusterConfig) {
132+
ctx := ensureE2EClusterContext(c)
133+
ctx.BasePort = port
134+
c.ClusterContext = ctx
135+
}
136+
}
137+
138+
func ensureE2EClusterContext(c *config.ClusterConfig) *e2e.ClusterContext {
139+
ctx, _ := c.ClusterContext.(*e2e.ClusterContext)
140+
if ctx == nil {
141+
ctx = &e2e.ClusterContext{}
142+
}
143+
return ctx
144+
}
145+
146+
func configureMirrorDestTLS(mm *config.MakeMirrorOptions, tls config.TLSConfig) {
147+
switch tls {
148+
case config.ManualTLS:
149+
mm.DestCert = e2e.CertPath
150+
mm.DestKey = e2e.PrivateKeyPath
151+
mm.DestCACert = e2e.CaPath
152+
mm.DestInsecureTransport = false
153+
default:
154+
mm.DestInsecureTransport = true
155+
}
124156
}

tests/common/integration_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,23 @@ func WithUnixClient() config.ClusterOption {
6565
}
6666

6767
func WithTCPClient() config.ClusterOption {
68-
return integration.WithTCPClient()
68+
return func(c *config.ClusterConfig) {
69+
ctx := ensureIntegrationClusterContext(c)
70+
ctx.UseUnix = false
71+
c.ClusterContext = ctx
72+
}
73+
}
74+
75+
func WithBasePort(port int) config.ClusterOption {
76+
return func(c *config.ClusterConfig) {}
6977
}
78+
79+
func ensureIntegrationClusterContext(c *config.ClusterConfig) *integration.ClusterContext {
80+
ctx, _ := c.ClusterContext.(*integration.ClusterContext)
81+
if ctx == nil {
82+
ctx = &integration.ClusterContext{}
83+
}
84+
return ctx
85+
}
86+
87+
func configureMirrorDestTLS(mm *config.MakeMirrorOptions, _ config.TLSConfig) {}

tests/common/make_mirror_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2016 The etcd Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package common
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"testing"
21+
"time"
22+
23+
"github.com/stretchr/testify/require"
24+
"go.etcd.io/etcd/tests/v3/framework/config"
25+
"go.etcd.io/etcd/tests/v3/framework/testutils"
26+
)
27+
28+
func TestMakeMirrorModifyDestPrefix(t *testing.T) {
29+
testRunner.BeforeTest(t)
30+
for _, srcTC := range clusterTestCases() {
31+
for _, destTC := range clusterTestCases() {
32+
t.Run(fmt.Sprintf("Source=%s/Destination=%s", srcTC.name, destTC.name), func(t *testing.T) {
33+
34+
var (
35+
mmOpts = config.MakeMirrorOptions{Prefix: "o_", DestPrefix: "d_"}
36+
sourcekvs = []testutils.KV{{Key: "o_key1", Val: "val1"}, {Key: "o_key2", Val: "val2"}, {Key: "o_key3", Val: "val3"}}
37+
destkvs = []testutils.KV{{Key: "d_key1", Val: "val1"}, {Key: "d_key2", Val: "val2"}, {Key: "d_key3", Val: "val3"}}
38+
srcprefix = "o_"
39+
destprefix = "d_"
40+
)
41+
42+
testMirror(t, srcTC, destTC, mmOpts, sourcekvs, destkvs, srcprefix, destprefix)
43+
})
44+
}
45+
}
46+
}
47+
48+
func TestMakeMirrorNoDestPrefix(t *testing.T) {
49+
testRunner.BeforeTest(t)
50+
for _, srcTC := range clusterTestCases() {
51+
for _, destTC := range clusterTestCases() {
52+
53+
t.Run(fmt.Sprintf("Source=%s/Destination=%s", srcTC.name, destTC.name), func(t *testing.T) {
54+
var (
55+
mmOpts = config.MakeMirrorOptions{Prefix: "o_", NoDestPrefix: true}
56+
sourcekvs = []testutils.KV{{Key: "o_key1", Val: "val1"}, {Key: "o_key2", Val: "val2"}, {Key: "o_key3", Val: "val3"}}
57+
destkvs = []testutils.KV{{Key: "key1", Val: "val1"}, {Key: "key2", Val: "val2"}, {Key: "key3", Val: "val3"}}
58+
srcprefix = "o_"
59+
destprefix = "key"
60+
)
61+
62+
testMirror(t, srcTC, destTC, mmOpts, sourcekvs, destkvs, srcprefix, destprefix)
63+
})
64+
}
65+
}
66+
}
67+
68+
func TestMakeMirror(t *testing.T) {
69+
testRunner.BeforeTest(t)
70+
for _, srcTC := range clusterTestCases() {
71+
for _, destTC := range clusterTestCases() {
72+
t.Run(fmt.Sprintf("Source=%s/Destination=%s", srcTC.name, destTC.name), func(t *testing.T) {
73+
var (
74+
mmOpts config.MakeMirrorOptions
75+
sourcekvs = []testutils.KV{{Key: "key1", Val: "val1"}, {Key: "key2", Val: "val2"}, {Key: "key3", Val: "val3"}}
76+
destkvs = []testutils.KV{{Key: "key1", Val: "val1"}, {Key: "key2", Val: "val2"}, {Key: "key3", Val: "val3"}}
77+
prefix = "key"
78+
)
79+
80+
testMirror(t, srcTC, destTC, mmOpts, sourcekvs, destkvs, prefix, prefix)
81+
82+
})
83+
}
84+
}
85+
}
86+
87+
func TestMakeMirrorWithWatchRev(t *testing.T) {
88+
testRunner.BeforeTest(t)
89+
for _, srcTC := range clusterTestCases() {
90+
for _, destTC := range clusterTestCases() {
91+
t.Run(fmt.Sprintf("Source=%s/Destination=%s", srcTC.name, destTC.name), func(t *testing.T) {
92+
var (
93+
mmOpts = config.MakeMirrorOptions{Prefix: "o_", NoDestPrefix: true, Rev: 4}
94+
sourcekvs = []testutils.KV{{Key: "o_key1", Val: "val1"}, {Key: "o_key2", Val: "val2"}, {Key: "o_key3", Val: "val3"}, {Key: "o_key4", Val: "val4"}}
95+
destkvs = []testutils.KV{{Key: "key3", Val: "val3"}, {Key: "key4", Val: "val4"}}
96+
srcprefix = "o_"
97+
destprefix = "key"
98+
)
99+
100+
testMirror(t, srcTC, destTC, mmOpts, sourcekvs, destkvs, srcprefix, destprefix)
101+
102+
})
103+
}
104+
}
105+
}
106+
107+
func testMirror(t *testing.T, srcTC, destTC testCase, mmOpts config.MakeMirrorOptions, sourcekvs, destkvs []testutils.KV, srcprefix, destprefix string) {
108+
t.Helper()
109+
110+
if destTC.config.ClientTLS == config.AutoTLS {
111+
// AutoTLS destination unsupported here: we cannot pass the destination CA to etcdctl (--dest-cacert)
112+
// so tLS verification would fail.
113+
t.Skip("Skipping: destingation uses Client AutoTLS, but the test cannot expose the dest CA for etcdctl (--dest-cacert); tLS verification would fail.")
114+
}
115+
116+
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
117+
defer cancel()
118+
// Source cluster
119+
src := testRunner.NewCluster(ctx, t, config.WithClusterConfig(srcTC.config))
120+
defer src.Close()
121+
srcClient := testutils.MustClient(src.Client())
122+
123+
// Destination cluster
124+
destCfg := destTC.config
125+
dest := testRunner.NewCluster(ctx, t, config.WithClusterConfig(destTC.config), WithBasePort(10000)) // destTC.BasePort
126+
defer dest.Close()
127+
destClient := testutils.MustClient(dest.Client())
128+
129+
// Configure TLS for destination before starting make-mirror
130+
configureMirrorDestTLS(&mmOpts, destCfg.ClientTLS)
131+
132+
// Start make mirror
133+
errCh := make(chan error)
134+
go func(opts config.MakeMirrorOptions) {
135+
errCh <- srcClient.MakeMirror(ctx, dest.Endpoints()[0], mmOpts)
136+
}(mmOpts)
137+
defer func() {
138+
// Need to cancel the context to ensure the MakeMirror goroutine is cancelled before catching the error.
139+
cancel()
140+
require.NoError(t, <-errCh)
141+
}()
142+
143+
// Write to source
144+
for i := range sourcekvs {
145+
require.NoError(t, srcClient.Put(ctx, sourcekvs[i].Key, sourcekvs[i].Val, config.PutOptions{}))
146+
}
147+
148+
// Source assertion
149+
srcResp, err := srcClient.Get(ctx, srcprefix, config.GetOptions{Prefix: true})
150+
require.NoError(t, err)
151+
require.Equal(t, sourcekvs, testutils.KeyValuesFromGetResponse(srcResp))
152+
153+
// Destination assertion
154+
wCtx, wCancel := context.WithCancel(ctx)
155+
defer wCancel()
156+
157+
watchChan := destClient.Watch(wCtx, destprefix, config.WatchOptions{Prefix: true, Revision: 1})
158+
159+
// Compare the result of what we obtained using the make-mirror command versus what we had using
160+
// the Watch command
161+
destResp, err := testutils.KeyValuesFromWatchChan(watchChan, len(destkvs), 10*time.Second)
162+
require.NoError(t, err)
163+
require.Equal(t, destkvs, destResp)
164+
}

tests/common/unit_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@ func WithTCPClient() config.ClusterOption {
5656
func WithUnixClient() config.ClusterOption {
5757
return func(c *config.ClusterConfig) {}
5858
}
59+
60+
func WithBasePort(port int) config.ClusterOption {
61+
return func(c *config.ClusterConfig) {}
62+
}

tests/framework/config/client.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,14 @@ type WatchOptions struct {
7777
Revision int64
7878
RangeEnd string
7979
}
80+
81+
type MakeMirrorOptions struct {
82+
Prefix string
83+
Rev int64
84+
DestPrefix string
85+
NoDestPrefix bool
86+
DestCACert string
87+
DestCert string
88+
DestKey string
89+
DestInsecureTransport bool
90+
}

tests/framework/e2e/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type ClusterContext struct {
4444
Version ClusterVersion
4545
EnvVars map[string]string
4646
UseUnix bool
47+
BasePort int
4748
}
4849

4950
func WithHTTP2Debug() config.ClusterOption {

tests/framework/e2e/e2e.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ func (e e2eRunner) NewCluster(ctx context.Context, tb testing.TB, opts ...config
6666
if ctx.UseUnix {
6767
e2eConfig.BaseClientScheme = "unix"
6868
}
69+
if ctx.BasePort != 0 {
70+
e2eConfig.BasePort = ctx.BasePort
71+
}
6972
}
7073

7174
switch cfg.ClientTLS {

tests/framework/e2e/etcdctl.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,3 +745,50 @@ func (ctl *EtcdctlV3) Watch(ctx context.Context, key string, opts config.WatchOp
745745

746746
return ch
747747
}
748+
749+
func (ctl *EtcdctlV3) MakeMirror(ctx context.Context, destEndpoints string, opts config.MakeMirrorOptions) error {
750+
751+
args := ctl.cmdArgs()
752+
args = append(args, "make-mirror")
753+
754+
if opts.Prefix != "" {
755+
args = append(args, "--prefix", opts.Prefix)
756+
}
757+
if opts.Rev != 0 {
758+
args = append(args, "--rev", fmt.Sprint(opts.Rev))
759+
}
760+
if opts.NoDestPrefix {
761+
args = append(args, "--no-dest-prefix")
762+
}
763+
if opts.DestPrefix != "" {
764+
args = append(args, "--dest-prefix", opts.DestPrefix)
765+
}
766+
if opts.DestCACert != "" {
767+
args = append(args, "--dest-cacert", opts.DestCACert)
768+
}
769+
if opts.DestCert != "" {
770+
args = append(args, "--dest-cert", opts.DestCert)
771+
}
772+
if opts.DestKey != "" {
773+
args = append(args, "--dest-key", opts.DestKey)
774+
}
775+
if opts.DestInsecureTransport {
776+
// Bool flags in cobra must be provided as --flag=false, not as separate args.
777+
args = append(args, "--dest-insecure-transport", "--dest-insecure-transport=false")
778+
}
779+
780+
args = append(args, destEndpoints)
781+
782+
proc, err := SpawnCmd(args, nil)
783+
if err != nil {
784+
return err
785+
}
786+
787+
defer proc.Stop()
788+
789+
// Wait until context is cancelled or its timeout is reached so that the make-mirror command can keep running in the background.
790+
<-ctx.Done()
791+
792+
return nil
793+
794+
}

tests/framework/integration/integration.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package integration
1616

1717
import (
1818
"context"
19+
"errors"
1920
"fmt"
2021
"strings"
2122
"testing"
@@ -475,3 +476,8 @@ func (c integrationClient) MemberList(ctx context.Context, serializable bool) (*
475476
}
476477
return c.Client.MemberList(ctx)
477478
}
479+
480+
func (c integrationClient) MakeMirror(ctx context.Context, destEndpoints string, opts config.MakeMirrorOptions) error {
481+
482+
return errors.New("error")
483+
}

tests/framework/interfaces/interface.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ type Client interface {
8484
MemberRemove(ctx context.Context, id uint64) (*clientv3.MemberRemoveResponse, error)
8585

8686
Watch(ctx context.Context, key string, opts config.WatchOptions) clientv3.WatchChan
87+
88+
MakeMirror(ctx context.Context, destEndpoints string, opts config.MakeMirrorOptions) error
8789
}
8890

8991
type TemplateEndpoints interface {

0 commit comments

Comments
 (0)