v3_curl_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  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. package e2e
  15. import (
  16. "encoding/base64"
  17. "encoding/json"
  18. "fmt"
  19. "path"
  20. "strconv"
  21. "testing"
  22. epb "github.com/coreos/etcd/etcdserver/api/v3election/v3electionpb"
  23. pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
  24. "github.com/coreos/etcd/pkg/testutil"
  25. "github.com/coreos/etcd/version"
  26. "github.com/grpc-ecosystem/grpc-gateway/runtime"
  27. )
  28. // TODO: remove /v3alpha tests in 3.4 release
  29. func TestV3CurlPutGetNoTLSAlpha(t *testing.T) { testCurlPutGetGRPCGateway(t, &configNoTLS, "/v3alpha") }
  30. func TestV3CurlPutGetNoTLSBeta(t *testing.T) { testCurlPutGetGRPCGateway(t, &configNoTLS, "/v3beta") }
  31. func TestV3CurlPutGetAutoTLSAlpha(t *testing.T) {
  32. testCurlPutGetGRPCGateway(t, &configAutoTLS, "/v3alpha")
  33. }
  34. func TestV3CurlPutGetAutoTLSBeta(t *testing.T) {
  35. testCurlPutGetGRPCGateway(t, &configAutoTLS, "/v3beta")
  36. }
  37. func TestV3CurlPutGetAllTLSAlpha(t *testing.T) { testCurlPutGetGRPCGateway(t, &configTLS, "/v3alpha") }
  38. func TestV3CurlPutGetAllTLSBeta(t *testing.T) { testCurlPutGetGRPCGateway(t, &configTLS, "/v3beta") }
  39. func TestV3CurlPutGetPeerTLSAlpha(t *testing.T) {
  40. testCurlPutGetGRPCGateway(t, &configPeerTLS, "/v3alpha")
  41. }
  42. func TestV3CurlPutGetPeerTLSBeta(t *testing.T) {
  43. testCurlPutGetGRPCGateway(t, &configPeerTLS, "/v3beta")
  44. }
  45. func TestV3CurlPutGetClientTLSAlpha(t *testing.T) {
  46. testCurlPutGetGRPCGateway(t, &configClientTLS, "/v3alpha")
  47. }
  48. func TestV3CurlPutGetClientTLSBeta(t *testing.T) {
  49. testCurlPutGetGRPCGateway(t, &configClientTLS, "/v3beta")
  50. }
  51. func testCurlPutGetGRPCGateway(t *testing.T, cfg *etcdProcessClusterConfig, pathPrefix string) {
  52. defer testutil.AfterTest(t)
  53. epc, err := newEtcdProcessCluster(cfg)
  54. if err != nil {
  55. t.Fatalf("could not start etcd process cluster (%v)", err)
  56. }
  57. defer func() {
  58. if cerr := epc.Close(); err != nil {
  59. t.Fatalf("error closing etcd processes (%v)", cerr)
  60. }
  61. }()
  62. var (
  63. key = []byte("foo")
  64. value = []byte("bar") // this will be automatically base64-encoded by Go
  65. expectPut = `"revision":"`
  66. expectGet = `"value":"`
  67. )
  68. putData, err := json.Marshal(&pb.PutRequest{
  69. Key: key,
  70. Value: value,
  71. })
  72. if err != nil {
  73. t.Fatal(err)
  74. }
  75. rangeData, err := json.Marshal(&pb.RangeRequest{
  76. Key: key,
  77. })
  78. if err != nil {
  79. t.Fatal(err)
  80. }
  81. if err := cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/put"), value: string(putData), expected: expectPut}); err != nil {
  82. t.Fatalf("failed put with curl (%v)", err)
  83. }
  84. if err := cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/range"), value: string(rangeData), expected: expectGet}); err != nil {
  85. t.Fatalf("failed get with curl (%v)", err)
  86. }
  87. if cfg.clientTLS == clientTLSAndNonTLS {
  88. if err := cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/range"), value: string(rangeData), expected: expectGet, isTLS: true}); err != nil {
  89. t.Fatalf("failed get with curl (%v)", err)
  90. }
  91. }
  92. }
  93. func TestV3CurlWatchAlpha(t *testing.T) { testV3CurlWatch(t, "/v3alpha") }
  94. func TestV3CurlWatchBeta(t *testing.T) { testV3CurlWatch(t, "/v3beta") }
  95. func testV3CurlWatch(t *testing.T, pathPrefix string) {
  96. defer testutil.AfterTest(t)
  97. epc, err := newEtcdProcessCluster(&configNoTLS)
  98. if err != nil {
  99. t.Fatalf("could not start etcd process cluster (%v)", err)
  100. }
  101. defer func() {
  102. if cerr := epc.Close(); err != nil {
  103. t.Fatalf("error closing etcd processes (%v)", cerr)
  104. }
  105. }()
  106. // store "bar" into "foo"
  107. putreq, err := json.Marshal(&pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
  108. if err != nil {
  109. t.Fatal(err)
  110. }
  111. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/put"), value: string(putreq), expected: "revision"}); err != nil {
  112. t.Fatalf("failed put with curl (%v)", err)
  113. }
  114. // watch for first update to "foo"
  115. wcr := &pb.WatchCreateRequest{Key: []byte("foo"), StartRevision: 1}
  116. wreq, err := json.Marshal(wcr)
  117. if err != nil {
  118. t.Fatal(err)
  119. }
  120. // marshaling the grpc to json gives:
  121. // "{"RequestUnion":{"CreateRequest":{"key":"Zm9v","start_revision":1}}}"
  122. // but the gprc-gateway expects a different format..
  123. wstr := `{"create_request" : ` + string(wreq) + "}"
  124. // expects "bar", timeout after 2 seconds since stream waits forever
  125. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/watch"), value: wstr, expected: `"YmFy"`, timeout: 2}); err != nil {
  126. t.Fatal(err)
  127. }
  128. }
  129. func TestV3CurlTxnAlpha(t *testing.T) { testV3CurlTxn(t, "/v3alpha") }
  130. func TestV3CurlTxnBeta(t *testing.T) { testV3CurlTxn(t, "/v3beta") }
  131. func testV3CurlTxn(t *testing.T, pathPrefix string) {
  132. defer testutil.AfterTest(t)
  133. epc, err := newEtcdProcessCluster(&configNoTLS)
  134. if err != nil {
  135. t.Fatalf("could not start etcd process cluster (%v)", err)
  136. }
  137. defer func() {
  138. if cerr := epc.Close(); err != nil {
  139. t.Fatalf("error closing etcd processes (%v)", cerr)
  140. }
  141. }()
  142. txn := &pb.TxnRequest{
  143. Compare: []*pb.Compare{
  144. {
  145. Key: []byte("foo"),
  146. Result: pb.Compare_EQUAL,
  147. Target: pb.Compare_CREATE,
  148. TargetUnion: &pb.Compare_CreateRevision{0},
  149. },
  150. },
  151. Success: []*pb.RequestOp{
  152. {
  153. Request: &pb.RequestOp_RequestPut{
  154. RequestPut: &pb.PutRequest{
  155. Key: []byte("foo"),
  156. Value: []byte("bar"),
  157. },
  158. },
  159. },
  160. },
  161. }
  162. m := &runtime.JSONPb{}
  163. jsonDat, jerr := m.Marshal(txn)
  164. if jerr != nil {
  165. t.Fatal(jerr)
  166. }
  167. expected := `"succeeded":true,"responses":[{"response_put":{"header":{"revision":"2"}}}]`
  168. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/txn"), value: string(jsonDat), expected: expected}); err != nil {
  169. t.Fatalf("failed txn with curl (%v)", err)
  170. }
  171. // was crashing etcd server
  172. malformed := `{"compare":[{"result":0,"target":1,"key":"Zm9v","TargetUnion":null}],"success":[{"Request":{"RequestPut":{"key":"Zm9v","value":"YmFy"}}}]}`
  173. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/txn"), value: malformed, expected: "error"}); err != nil {
  174. t.Fatalf("failed put with curl (%v)", err)
  175. }
  176. }
  177. func TestV3CurlAuthAlpha(t *testing.T) { testV3CurlAuth(t, "/v3alpha") }
  178. func TestV3CurlAuthBeta(t *testing.T) { testV3CurlAuth(t, "/v3beta") }
  179. func testV3CurlAuth(t *testing.T, pathPrefix string) {
  180. defer testutil.AfterTest(t)
  181. epc, err := newEtcdProcessCluster(&configNoTLS)
  182. if err != nil {
  183. t.Fatalf("could not start etcd process cluster (%v)", err)
  184. }
  185. defer func() {
  186. if cerr := epc.Close(); err != nil {
  187. t.Fatalf("error closing etcd processes (%v)", cerr)
  188. }
  189. }()
  190. // create root user
  191. userreq, err := json.Marshal(&pb.AuthUserAddRequest{Name: string("root"), Password: string("toor")})
  192. testutil.AssertNil(t, err)
  193. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/auth/user/add"), value: string(userreq), expected: "revision"}); err != nil {
  194. t.Fatalf("failed add user with curl (%v)", err)
  195. }
  196. // create root role
  197. rolereq, err := json.Marshal(&pb.AuthRoleAddRequest{Name: string("root")})
  198. testutil.AssertNil(t, err)
  199. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/auth/role/add"), value: string(rolereq), expected: "revision"}); err != nil {
  200. t.Fatalf("failed create role with curl (%v)", err)
  201. }
  202. // grant root role
  203. grantrolereq, err := json.Marshal(&pb.AuthUserGrantRoleRequest{User: string("root"), Role: string("root")})
  204. testutil.AssertNil(t, err)
  205. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/auth/user/grant"), value: string(grantrolereq), expected: "revision"}); err != nil {
  206. t.Fatalf("failed grant role with curl (%v)", err)
  207. }
  208. // enable auth
  209. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/auth/enable"), value: string("{}"), expected: "revision"}); err != nil {
  210. t.Fatalf("failed enable auth with curl (%v)", err)
  211. }
  212. // put "bar" into "foo"
  213. putreq, err := json.Marshal(&pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
  214. testutil.AssertNil(t, err)
  215. // fail put no auth
  216. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/put"), value: string(putreq), expected: "error"}); err != nil {
  217. t.Fatalf("failed no auth put with curl (%v)", err)
  218. }
  219. // auth request
  220. authreq, err := json.Marshal(&pb.AuthenticateRequest{Name: string("root"), Password: string("toor")})
  221. testutil.AssertNil(t, err)
  222. var (
  223. authHeader string
  224. cmdArgs []string
  225. lineFunc = func(txt string) bool { return true }
  226. )
  227. cmdArgs = cURLPrefixArgs(epc, "POST", cURLReq{endpoint: path.Join(pathPrefix, "/auth/authenticate"), value: string(authreq)})
  228. proc, err := spawnCmd(cmdArgs)
  229. testutil.AssertNil(t, err)
  230. cURLRes, err := proc.ExpectFunc(lineFunc)
  231. testutil.AssertNil(t, err)
  232. authRes := make(map[string]interface{})
  233. testutil.AssertNil(t, json.Unmarshal([]byte(cURLRes), &authRes))
  234. token, ok := authRes["token"].(string)
  235. if !ok {
  236. t.Fatalf("failed invalid token in authenticate response with curl")
  237. }
  238. authHeader = "Authorization : " + token
  239. // put with auth
  240. if err = cURLPost(epc, cURLReq{endpoint: path.Join(pathPrefix, "/kv/put"), value: string(putreq), header: authHeader, expected: "revision"}); err != nil {
  241. t.Fatalf("failed auth put with curl (%v)", err)
  242. }
  243. }
  244. func TestV3CurlCampaignAlpha(t *testing.T) { testV3CurlCampaign(t, "/v3alpha") }
  245. func TestV3CurlCampaignBeta(t *testing.T) { testV3CurlCampaign(t, "/v3beta") }
  246. func testV3CurlCampaign(t *testing.T, pathPrefix string) {
  247. defer testutil.AfterTest(t)
  248. epc, err := newEtcdProcessCluster(&configNoTLS)
  249. if err != nil {
  250. t.Fatalf("could not start etcd process cluster (%v)", err)
  251. }
  252. defer func() {
  253. if cerr := epc.Close(); err != nil {
  254. t.Fatalf("error closing etcd processes (%v)", cerr)
  255. }
  256. }()
  257. cdata, err := json.Marshal(&epb.CampaignRequest{
  258. Name: []byte("/election-prefix"),
  259. Value: []byte("v1"),
  260. })
  261. if err != nil {
  262. t.Fatal(err)
  263. }
  264. cargs := cURLPrefixArgs(epc, "POST", cURLReq{
  265. endpoint: path.Join(pathPrefix, "/election/campaign"),
  266. value: string(cdata),
  267. })
  268. lines, err := spawnWithExpectLines(cargs, `"leader":{"name":"`)
  269. if err != nil {
  270. t.Fatalf("failed post campaign request (%s) (%v)", pathPrefix, err)
  271. }
  272. if len(lines) != 1 {
  273. t.Fatalf("len(lines) expected 1, got %+v", lines)
  274. }
  275. var cresp campaignResponse
  276. if err = json.Unmarshal([]byte(lines[0]), &cresp); err != nil {
  277. t.Fatalf("failed to unmarshal campaign response %v", err)
  278. }
  279. ndata, err := base64.StdEncoding.DecodeString(cresp.Leader.Name)
  280. if err != nil {
  281. t.Fatalf("failed to decode leader key %v", err)
  282. }
  283. kdata, err := base64.StdEncoding.DecodeString(cresp.Leader.Key)
  284. if err != nil {
  285. t.Fatalf("failed to decode leader key %v", err)
  286. }
  287. rev, _ := strconv.ParseInt(cresp.Leader.Rev, 10, 64)
  288. lease, _ := strconv.ParseInt(cresp.Leader.Lease, 10, 64)
  289. pdata, err := json.Marshal(&epb.ProclaimRequest{
  290. Leader: &epb.LeaderKey{
  291. Name: ndata,
  292. Key: kdata,
  293. Rev: rev,
  294. Lease: lease,
  295. },
  296. Value: []byte("v2"),
  297. })
  298. if err != nil {
  299. t.Fatal(err)
  300. }
  301. if err = cURLPost(epc, cURLReq{
  302. endpoint: path.Join(pathPrefix, "/election/proclaim"),
  303. value: string(pdata),
  304. expected: `"revision":`,
  305. }); err != nil {
  306. t.Fatalf("failed post proclaim request (%s) (%v)", pathPrefix, err)
  307. }
  308. }
  309. func TestV3CurlProclaimMissiongLeaderKeyNoTLS(t *testing.T) {
  310. testCtl(t, testV3CurlProclaimMissiongLeaderKey, withCfg(configNoTLS))
  311. }
  312. func testV3CurlProclaimMissiongLeaderKey(cx ctlCtx) {
  313. pdata, err := json.Marshal(&epb.ProclaimRequest{Value: []byte("v2")})
  314. if err != nil {
  315. cx.t.Fatal(err)
  316. }
  317. if err != nil {
  318. cx.t.Fatal(err)
  319. }
  320. if err = cURLPost(cx.epc, cURLReq{
  321. endpoint: path.Join("/v3beta", "/election/proclaim"),
  322. value: string(pdata),
  323. expected: `{"error":"\"leader\" field must be provided","code":2}`,
  324. }); err != nil {
  325. cx.t.Fatalf("failed post proclaim request (%s) (%v)", "/v3beta", err)
  326. }
  327. }
  328. func TestV3CurlResignMissiongLeaderKeyNoTLS(t *testing.T) {
  329. testCtl(t, testV3CurlResignMissiongLeaderKey, withCfg(configNoTLS))
  330. }
  331. func testV3CurlResignMissiongLeaderKey(cx ctlCtx) {
  332. if err := cURLPost(cx.epc, cURLReq{
  333. endpoint: path.Join("/v3beta", "/election/resign"),
  334. value: `{}`,
  335. expected: `{"error":"\"leader\" field must be provided","code":2}`,
  336. }); err != nil {
  337. cx.t.Fatalf("failed post resign request (%s) (%v)", "/v3beta", err)
  338. }
  339. }
  340. // to manually decode; JSON marshals integer fields with
  341. // string types, so can't unmarshal with epb.CampaignResponse
  342. type campaignResponse struct {
  343. Leader struct {
  344. Name string `json:"name,omitempty"`
  345. Key string `json:"key,omitempty"`
  346. Rev string `json:"rev,omitempty"`
  347. Lease string `json:"lease,omitempty"`
  348. } `json:"leader,omitempty"`
  349. }
  350. func TestV3CurlCipherSuitesValid(t *testing.T) { testV3CurlCipherSuites(t, true) }
  351. func TestV3CurlCipherSuitesMismatch(t *testing.T) { testV3CurlCipherSuites(t, false) }
  352. func testV3CurlCipherSuites(t *testing.T, valid bool) {
  353. cc := configClientTLS
  354. cc.clusterSize = 1
  355. cc.cipherSuites = []string{
  356. "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
  357. "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
  358. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
  359. "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
  360. "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
  361. "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
  362. }
  363. testFunc := cipherSuiteTestValid
  364. if !valid {
  365. testFunc = cipherSuiteTestMismatch
  366. }
  367. testCtl(t, testFunc, withCfg(cc))
  368. }
  369. func cipherSuiteTestValid(cx ctlCtx) {
  370. if err := cURLGet(cx.epc, cURLReq{
  371. endpoint: "/metrics",
  372. expected: fmt.Sprintf(`etcd_server_version{server_version="%s"} 1`, version.Version),
  373. metricsURLScheme: cx.cfg.metricsURLScheme,
  374. ciphers: "ECDHE-RSA-AES128-GCM-SHA256", // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  375. }); err != nil {
  376. cx.t.Logf("failed get with curl (%v)", err)
  377. }
  378. }
  379. func cipherSuiteTestMismatch(cx ctlCtx) {
  380. if err := cURLGet(cx.epc, cURLReq{
  381. endpoint: "/metrics",
  382. expected: "alert handshake failure",
  383. metricsURLScheme: cx.cfg.metricsURLScheme,
  384. ciphers: "ECDHE-RSA-DES-CBC3-SHA", // TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
  385. }); err != nil {
  386. cx.t.Logf("failed get with curl (%v)", err)
  387. }
  388. }