1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15- // These tests depends on certificate-based authentication that is NOT supported
15+ // These tests depend on certificate-based authentication that is NOT supported
1616// by gRPC proxy.
1717//go:build !cluster_proxy
1818// +build !cluster_proxy
1919
2020package e2e
2121
2222import (
23+ "fmt"
24+ "sync"
2325 "testing"
26+ "time"
2427)
2528
2629func TestCtlV3AuthCertCN (t * testing.T ) {
@@ -32,3 +35,106 @@ func TestCtlV3AuthCertCNAndUsername(t *testing.T) {
3235func TestCtlV3AuthCertCNAndUsernameNoPassword (t * testing.T ) {
3336 testCtl (t , authTestCertCNAndUsernameNoPassword , withCfg (* newConfigClientTLSCertAuth ()))
3437}
38+
39+ func TestCtlV3AuthCertCNWithWithConcurrentOperation (t * testing.T ) {
40+ BeforeTest (t )
41+
42+ // apply the certificate which has `root` CommonName,
43+ // and reset the setting when the test case finishes.
44+ // TODO(ahrtr): enhance the e2e test framework to support
45+ // certificates with CommonName.
46+ t .Log ("Apply certificate with root CommonName" )
47+ resetCert := applyTLSWithRootCommonName ()
48+ defer resetCert ()
49+
50+ t .Log ("Create an etcd cluster" )
51+ cx := getDefaultCtlCtx (t )
52+ cx .cfg = etcdProcessClusterConfig {
53+ clusterSize : 1 ,
54+ clientTLS : clientTLS ,
55+ clientCertAuthEnabled : true ,
56+ initialToken : "new" ,
57+ }
58+
59+ epc , err := newEtcdProcessCluster (t , & cx .cfg )
60+ if err != nil {
61+ t .Fatalf ("Failed to start etcd cluster: %v" , err )
62+ }
63+ cx .epc = epc
64+ cx .dataDir = epc .procs [0 ].Config ().dataDirPath
65+
66+ defer func () {
67+ if err := epc .Close (); err != nil {
68+ t .Fatalf ("could not close test cluster (%v)" , err )
69+ }
70+ }()
71+
72+ t .Log ("Enable auth" )
73+ authEnableTest (cx )
74+
75+ // Create two goroutines, one goroutine keeps creating & deleting users,
76+ // and the other goroutine keeps writing & deleting K/V entries.
77+ var wg sync.WaitGroup
78+ wg .Add (2 )
79+ errs := make (chan error , 2 )
80+ donec := make (chan struct {})
81+
82+ // Create the first goroutine to create & delete users
83+ t .Log ("Create the first goroutine to create & delete users" )
84+ go func () {
85+ defer wg .Done ()
86+ for i := 0 ; i < 100 ; i ++ {
87+ user := fmt .Sprintf ("testuser-%d" , i )
88+ pass := fmt .Sprintf ("testpass-%d" , i )
89+
90+ if err := ctlV3User (cx , []string {"add" , user , "--interactive=false" }, fmt .Sprintf ("User %s created" , user ), []string {pass }); err != nil {
91+ errs <- fmt .Errorf ("failed to create user %q: %w" , user , err )
92+ break
93+ }
94+
95+ err := ctlV3User (cx , []string {"delete" , user }, fmt .Sprintf ("User %s deleted" , user ), []string {})
96+ if err != nil {
97+ errs <- fmt .Errorf ("failed to delete user %q: %w" , user , err )
98+ break
99+ }
100+ }
101+ t .Log ("The first goroutine finished" )
102+ }()
103+
104+ // Create the second goroutine to write & delete K/V entries
105+ t .Log ("Create the second goroutine to write & delete K/V entries" )
106+ go func () {
107+ defer wg .Done ()
108+ for i := 0 ; i < 100 ; i ++ {
109+ key := fmt .Sprintf ("key-%d" , i )
110+ value := fmt .Sprintf ("value-%d" , i )
111+
112+ if err := ctlV3Put (cx , key , value , "" ); err != nil {
113+ errs <- fmt .Errorf ("failed to put key %q: %w" , key , err )
114+ break
115+ }
116+
117+ if err := ctlV3Del (cx , []string {key }, 1 ); err != nil {
118+ errs <- fmt .Errorf ("failed to delete key %q: %w" , key , err )
119+ break
120+ }
121+ }
122+ t .Log ("The second goroutine finished" )
123+ }()
124+
125+ t .Log ("Waiting for the two goroutines to complete" )
126+ go func () {
127+ wg .Wait ()
128+ close (donec )
129+ }()
130+
131+ t .Log ("Waiting for test result" )
132+ select {
133+ case err := <- errs :
134+ t .Fatalf ("Unexpected error: %v" , err )
135+ case <- donec :
136+ t .Log ("All done!" )
137+ case <- time .After (60 * time .Second ):
138+ t .Fatal ("Test case timeout after 60 seconds" )
139+ }
140+ }
0 commit comments