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 15package e2e 16 17import ( 18 "fmt" 19 "strings" 20 "testing" 21 "time" 22) 23 24func TestCtlV3Put(t *testing.T) { testCtl(t, putTest, withDialTimeout(7*time.Second)) } 25func TestCtlV3PutNoTLS(t *testing.T) { testCtl(t, putTest, withCfg(configNoTLS)) } 26func TestCtlV3PutClientTLS(t *testing.T) { testCtl(t, putTest, withCfg(configClientTLS)) } 27func TestCtlV3PutClientAutoTLS(t *testing.T) { testCtl(t, putTest, withCfg(configClientAutoTLS)) } 28func TestCtlV3PutPeerTLS(t *testing.T) { testCtl(t, putTest, withCfg(configPeerTLS)) } 29func TestCtlV3PutTimeout(t *testing.T) { testCtl(t, putTest, withDialTimeout(0)) } 30func TestCtlV3PutClientTLSFlagByEnv(t *testing.T) { 31 testCtl(t, putTest, withCfg(configClientTLS), withFlagByEnv()) 32} 33func TestCtlV3PutIgnoreValue(t *testing.T) { testCtl(t, putTestIgnoreValue) } 34func TestCtlV3PutIgnoreLease(t *testing.T) { testCtl(t, putTestIgnoreLease) } 35 36func TestCtlV3Get(t *testing.T) { testCtl(t, getTest) } 37func TestCtlV3GetNoTLS(t *testing.T) { testCtl(t, getTest, withCfg(configNoTLS)) } 38func TestCtlV3GetClientTLS(t *testing.T) { testCtl(t, getTest, withCfg(configClientTLS)) } 39func TestCtlV3GetClientAutoTLS(t *testing.T) { testCtl(t, getTest, withCfg(configClientAutoTLS)) } 40func TestCtlV3GetPeerTLS(t *testing.T) { testCtl(t, getTest, withCfg(configPeerTLS)) } 41func TestCtlV3GetTimeout(t *testing.T) { testCtl(t, getTest, withDialTimeout(0)) } 42func TestCtlV3GetQuorum(t *testing.T) { testCtl(t, getTest, withQuorum()) } 43 44func TestCtlV3GetFormat(t *testing.T) { testCtl(t, getFormatTest) } 45func TestCtlV3GetRev(t *testing.T) { testCtl(t, getRevTest) } 46func TestCtlV3GetKeysOnly(t *testing.T) { testCtl(t, getKeysOnlyTest) } 47func TestCtlV3GetCountOnly(t *testing.T) { testCtl(t, getCountOnlyTest) } 48 49func TestCtlV3Del(t *testing.T) { testCtl(t, delTest) } 50func TestCtlV3DelNoTLS(t *testing.T) { testCtl(t, delTest, withCfg(configNoTLS)) } 51func TestCtlV3DelClientTLS(t *testing.T) { testCtl(t, delTest, withCfg(configClientTLS)) } 52func TestCtlV3DelPeerTLS(t *testing.T) { testCtl(t, delTest, withCfg(configPeerTLS)) } 53func TestCtlV3DelTimeout(t *testing.T) { testCtl(t, delTest, withDialTimeout(0)) } 54 55func TestCtlV3GetRevokedCRL(t *testing.T) { 56 cfg := etcdProcessClusterConfig{ 57 clusterSize: 1, 58 initialToken: "new", 59 clientTLS: clientTLS, 60 isClientCRL: true, 61 clientCertAuthEnabled: true, 62 } 63 testCtl(t, testGetRevokedCRL, withCfg(cfg)) 64} 65 66func testGetRevokedCRL(cx ctlCtx) { 67 // test reject 68 if err := ctlV3Put(cx, "k", "v", ""); err == nil || !strings.Contains(err.Error(), "Error:") { 69 cx.t.Fatalf("expected reset connection on put, got %v", err) 70 } 71 // test accept 72 cx.epc.cfg.isClientCRL = false 73 if err := ctlV3Put(cx, "k", "v", ""); err != nil { 74 cx.t.Fatal(err) 75 } 76} 77 78func putTest(cx ctlCtx) { 79 key, value := "foo", "bar" 80 81 if err := ctlV3Put(cx, key, value, ""); err != nil { 82 if cx.dialTimeout > 0 && !isGRPCTimedout(err) { 83 cx.t.Fatalf("putTest ctlV3Put error (%v)", err) 84 } 85 } 86 if err := ctlV3Get(cx, []string{key}, kv{key, value}); err != nil { 87 if cx.dialTimeout > 0 && !isGRPCTimedout(err) { 88 cx.t.Fatalf("putTest ctlV3Get error (%v)", err) 89 } 90 } 91} 92 93func putTestIgnoreValue(cx ctlCtx) { 94 if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { 95 cx.t.Fatal(err) 96 } 97 if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar"}); err != nil { 98 cx.t.Fatal(err) 99 } 100 if err := ctlV3Put(cx, "foo", "", "", "--ignore-value"); err != nil { 101 cx.t.Fatal(err) 102 } 103 if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar"}); err != nil { 104 cx.t.Fatal(err) 105 } 106} 107 108func putTestIgnoreLease(cx ctlCtx) { 109 leaseID, err := ctlV3LeaseGrant(cx, 10) 110 if err != nil { 111 cx.t.Fatalf("putTestIgnoreLease: ctlV3LeaseGrant error (%v)", err) 112 } 113 if err := ctlV3Put(cx, "foo", "bar", leaseID); err != nil { 114 cx.t.Fatalf("putTestIgnoreLease: ctlV3Put error (%v)", err) 115 } 116 if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar"}); err != nil { 117 cx.t.Fatalf("putTestIgnoreLease: ctlV3Get error (%v)", err) 118 } 119 if err := ctlV3Put(cx, "foo", "bar1", "", "--ignore-lease"); err != nil { 120 cx.t.Fatalf("putTestIgnoreLease: ctlV3Put error (%v)", err) 121 } 122 if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar1"}); err != nil { 123 cx.t.Fatalf("putTestIgnoreLease: ctlV3Get error (%v)", err) 124 } 125 if err := ctlV3LeaseRevoke(cx, leaseID); err != nil { 126 cx.t.Fatalf("putTestIgnoreLease: ctlV3LeaseRevok error (%v)", err) 127 } 128 if err := ctlV3Get(cx, []string{"key"}); err != nil { // expect no output 129 cx.t.Fatalf("putTestIgnoreLease: ctlV3Get error (%v)", err) 130 } 131} 132 133func getTest(cx ctlCtx) { 134 var ( 135 kvs = []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} 136 revkvs = []kv{{"key3", "val3"}, {"key2", "val2"}, {"key1", "val1"}} 137 ) 138 for i := range kvs { 139 if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { 140 cx.t.Fatalf("getTest #%d: ctlV3Put error (%v)", i, err) 141 } 142 } 143 144 tests := []struct { 145 args []string 146 147 wkv []kv 148 }{ 149 {[]string{"key1"}, []kv{{"key1", "val1"}}}, 150 {[]string{"", "--prefix"}, kvs}, 151 {[]string{"", "--from-key"}, kvs}, 152 {[]string{"key", "--prefix"}, kvs}, 153 {[]string{"key", "--prefix", "--limit=2"}, kvs[:2]}, 154 {[]string{"key", "--prefix", "--order=ASCEND", "--sort-by=MODIFY"}, kvs}, 155 {[]string{"key", "--prefix", "--order=ASCEND", "--sort-by=VERSION"}, kvs}, 156 {[]string{"key", "--prefix", "--sort-by=CREATE"}, kvs}, // ASCEND by default 157 {[]string{"key", "--prefix", "--order=DESCEND", "--sort-by=CREATE"}, revkvs}, 158 {[]string{"key", "--prefix", "--order=DESCEND", "--sort-by=KEY"}, revkvs}, 159 } 160 for i, tt := range tests { 161 if err := ctlV3Get(cx, tt.args, tt.wkv...); err != nil { 162 if cx.dialTimeout > 0 && !isGRPCTimedout(err) { 163 cx.t.Errorf("getTest #%d: ctlV3Get error (%v)", i, err) 164 } 165 } 166 } 167} 168 169func getFormatTest(cx ctlCtx) { 170 if err := ctlV3Put(cx, "abc", "123", ""); err != nil { 171 cx.t.Fatal(err) 172 } 173 174 tests := []struct { 175 format string 176 valueOnly bool 177 178 wstr string 179 }{ 180 {"simple", false, "abc"}, 181 {"simple", true, "123"}, 182 {"json", false, `"kvs":[{"key":"YWJj"`}, 183 {"protobuf", false, "\x17\b\x93\xe7\xf6\x93\xd4ņ\xe14\x10\xed"}, 184 } 185 186 for i, tt := range tests { 187 cmdArgs := append(cx.PrefixArgs(), "get") 188 cmdArgs = append(cmdArgs, "--write-out="+tt.format) 189 if tt.valueOnly { 190 cmdArgs = append(cmdArgs, "--print-value-only") 191 } 192 cmdArgs = append(cmdArgs, "abc") 193 if err := spawnWithExpect(cmdArgs, tt.wstr); err != nil { 194 cx.t.Errorf("#%d: error (%v), wanted %v", i, err, tt.wstr) 195 } 196 } 197} 198 199func getRevTest(cx ctlCtx) { 200 var ( 201 kvs = []kv{{"key", "val1"}, {"key", "val2"}, {"key", "val3"}} 202 ) 203 for i := range kvs { 204 if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { 205 cx.t.Fatalf("getRevTest #%d: ctlV3Put error (%v)", i, err) 206 } 207 } 208 209 tests := []struct { 210 args []string 211 212 wkv []kv 213 }{ 214 {[]string{"key", "--rev", "2"}, kvs[:1]}, 215 {[]string{"key", "--rev", "3"}, kvs[1:2]}, 216 {[]string{"key", "--rev", "4"}, kvs[2:]}, 217 } 218 219 for i, tt := range tests { 220 if err := ctlV3Get(cx, tt.args, tt.wkv...); err != nil { 221 cx.t.Errorf("getTest #%d: ctlV3Get error (%v)", i, err) 222 } 223 } 224} 225 226func getKeysOnlyTest(cx ctlCtx) { 227 if err := ctlV3Put(cx, "key", "val", ""); err != nil { 228 cx.t.Fatal(err) 229 } 230 cmdArgs := append(cx.PrefixArgs(), []string{"get", "--keys-only", "key"}...) 231 if err := spawnWithExpect(cmdArgs, "key"); err != nil { 232 cx.t.Fatal(err) 233 } 234 if err := spawnWithExpects(cmdArgs, "val"); err == nil { 235 cx.t.Fatalf("got value but passed --keys-only") 236 } 237} 238 239func getCountOnlyTest(cx ctlCtx) { 240 cmdArgs := append(cx.PrefixArgs(), []string{"get", "--count-only", "key", "--prefix", "--write-out=fields"}...) 241 if err := spawnWithExpects(cmdArgs, "\"Count\" : 0"); err != nil { 242 cx.t.Fatal(err) 243 } 244 if err := ctlV3Put(cx, "key", "val", ""); err != nil { 245 cx.t.Fatal(err) 246 } 247 cmdArgs = append(cx.PrefixArgs(), []string{"get", "--count-only", "key", "--prefix", "--write-out=fields"}...) 248 if err := spawnWithExpects(cmdArgs, "\"Count\" : 1"); err != nil { 249 cx.t.Fatal(err) 250 } 251 if err := ctlV3Put(cx, "key1", "val", ""); err != nil { 252 cx.t.Fatal(err) 253 } 254 if err := ctlV3Put(cx, "key1", "val", ""); err != nil { 255 cx.t.Fatal(err) 256 } 257 cmdArgs = append(cx.PrefixArgs(), []string{"get", "--count-only", "key", "--prefix", "--write-out=fields"}...) 258 if err := spawnWithExpects(cmdArgs, "\"Count\" : 2"); err != nil { 259 cx.t.Fatal(err) 260 } 261 if err := ctlV3Put(cx, "key2", "val", ""); err != nil { 262 cx.t.Fatal(err) 263 } 264 cmdArgs = append(cx.PrefixArgs(), []string{"get", "--count-only", "key", "--prefix", "--write-out=fields"}...) 265 if err := spawnWithExpects(cmdArgs, "\"Count\" : 3"); err != nil { 266 cx.t.Fatal(err) 267 } 268 expected := []string{ 269 "\"Count\" : 3", 270 } 271 cmdArgs = append(cx.PrefixArgs(), []string{"get", "--count-only", "key3", "--prefix", "--write-out=fields"}...) 272 if err := spawnWithExpects(cmdArgs, expected...); err == nil { 273 cx.t.Fatal(err) 274 } 275} 276 277func delTest(cx ctlCtx) { 278 tests := []struct { 279 puts []kv 280 args []string 281 282 deletedNum int 283 }{ 284 { // delete all keys 285 []kv{{"foo1", "bar"}, {"foo2", "bar"}, {"foo3", "bar"}}, 286 []string{"", "--prefix"}, 287 3, 288 }, 289 { // delete all keys 290 []kv{{"foo1", "bar"}, {"foo2", "bar"}, {"foo3", "bar"}}, 291 []string{"", "--from-key"}, 292 3, 293 }, 294 { 295 []kv{{"this", "value"}}, 296 []string{"that"}, 297 0, 298 }, 299 { 300 []kv{{"sample", "value"}}, 301 []string{"sample"}, 302 1, 303 }, 304 { 305 []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}}, 306 []string{"key", "--prefix"}, 307 3, 308 }, 309 { 310 []kv{{"zoo1", "bar"}, {"zoo2", "bar2"}, {"zoo3", "bar3"}}, 311 []string{"zoo1", "--from-key"}, 312 3, 313 }, 314 } 315 316 for i, tt := range tests { 317 for j := range tt.puts { 318 if err := ctlV3Put(cx, tt.puts[j].key, tt.puts[j].val, ""); err != nil { 319 cx.t.Fatalf("delTest #%d-%d: ctlV3Put error (%v)", i, j, err) 320 } 321 } 322 if err := ctlV3Del(cx, tt.args, tt.deletedNum); err != nil { 323 if cx.dialTimeout > 0 && !isGRPCTimedout(err) { 324 cx.t.Fatalf("delTest #%d: ctlV3Del error (%v)", i, err) 325 } 326 } 327 } 328} 329 330func ctlV3Put(cx ctlCtx, key, value, leaseID string, flags ...string) error { 331 skipValue := false 332 skipLease := false 333 for _, f := range flags { 334 if f == "--ignore-value" { 335 skipValue = true 336 } 337 if f == "--ignore-lease" { 338 skipLease = true 339 } 340 } 341 cmdArgs := append(cx.PrefixArgs(), "put", key) 342 if !skipValue { 343 cmdArgs = append(cmdArgs, value) 344 } 345 if leaseID != "" && !skipLease { 346 cmdArgs = append(cmdArgs, "--lease", leaseID) 347 } 348 if len(flags) != 0 { 349 cmdArgs = append(cmdArgs, flags...) 350 } 351 return spawnWithExpect(cmdArgs, "OK") 352} 353 354type kv struct { 355 key, val string 356} 357 358func ctlV3Get(cx ctlCtx, args []string, kvs ...kv) error { 359 cmdArgs := append(cx.PrefixArgs(), "get") 360 cmdArgs = append(cmdArgs, args...) 361 if !cx.quorum { 362 cmdArgs = append(cmdArgs, "--consistency", "s") 363 } 364 var lines []string 365 for _, elem := range kvs { 366 lines = append(lines, elem.key, elem.val) 367 } 368 return spawnWithExpects(cmdArgs, lines...) 369} 370 371// ctlV3GetWithErr runs "get" command expecting no output but error 372func ctlV3GetWithErr(cx ctlCtx, args []string, errs []string) error { 373 cmdArgs := append(cx.PrefixArgs(), "get") 374 cmdArgs = append(cmdArgs, args...) 375 if !cx.quorum { 376 cmdArgs = append(cmdArgs, "--consistency", "s") 377 } 378 return spawnWithExpects(cmdArgs, errs...) 379} 380 381func ctlV3Del(cx ctlCtx, args []string, num int) error { 382 cmdArgs := append(cx.PrefixArgs(), "del") 383 cmdArgs = append(cmdArgs, args...) 384 return spawnWithExpects(cmdArgs, fmt.Sprintf("%d", num)) 385} 386