endpoints.Interpret returns Host:port as ServerName

Signed-off-by: Chao Chen <chaochn@amazon.com>
This commit is contained in:
Chao Chen 2023-07-27 15:18:12 -07:00
parent e59e3d709c
commit 8aeed09f2c
4 changed files with 54 additions and 60 deletions

View File

@ -41,10 +41,6 @@ func extractHostFromHostPort(ep string) string {
return host return host
} }
func extractHostFromPath(pathStr string) string {
return extractHostFromHostPort(path.Base(pathStr))
}
// mustSplit2 returns the values from strings.SplitN(s, sep, 2). // mustSplit2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", "", false) instead. // If sep is not found, it returns ("", "", false) instead.
func mustSplit2(s, sep string) (string, string) { func mustSplit2(s, sep string) (string, string) {
@ -96,29 +92,29 @@ func translateEndpoint(ep string) (addr string, serverName string, requireCreds
if strings.HasPrefix(ep, "unix:///") || strings.HasPrefix(ep, "unixs:///") { if strings.HasPrefix(ep, "unix:///") || strings.HasPrefix(ep, "unixs:///") {
// absolute path case // absolute path case
schema, absolutePath := mustSplit2(ep, "://") schema, absolutePath := mustSplit2(ep, "://")
return "unix://" + absolutePath, extractHostFromPath(absolutePath), schemeToCredsRequirement(schema) return "unix://" + absolutePath, path.Base(absolutePath), schemeToCredsRequirement(schema)
} }
if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") { if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") {
// legacy etcd local path // legacy etcd local path
schema, localPath := mustSplit2(ep, "://") schema, localPath := mustSplit2(ep, "://")
return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema) return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema)
} }
schema, localPath := mustSplit2(ep, ":") schema, localPath := mustSplit2(ep, ":")
return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema) return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema)
} }
if strings.Contains(ep, "://") { if strings.Contains(ep, "://") {
url, err := url.Parse(ep) url, err := url.Parse(ep)
if err != nil { if err != nil {
return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL return ep, ep, CREDS_OPTIONAL
} }
if url.Scheme == "http" || url.Scheme == "https" { if url.Scheme == "http" || url.Scheme == "https" {
return url.Host, url.Hostname(), schemeToCredsRequirement(url.Scheme) return url.Host, url.Host, schemeToCredsRequirement(url.Scheme)
} }
return ep, url.Hostname(), schemeToCredsRequirement(url.Scheme) return ep, url.Host, schemeToCredsRequirement(url.Scheme)
} }
// Handles plain addresses like 10.0.0.44:437. // Handles plain addresses like 10.0.0.44:437.
return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL return ep, ep, CREDS_OPTIONAL
} }
// RequiresCredentials returns whether given endpoint requires // RequiresCredentials returns whether given endpoint requires

View File

@ -27,35 +27,35 @@ func Test_interpret(t *testing.T) {
}{ }{
{"127.0.0.1", "127.0.0.1", "127.0.0.1", CREDS_OPTIONAL}, {"127.0.0.1", "127.0.0.1", "127.0.0.1", CREDS_OPTIONAL},
{"localhost", "localhost", "localhost", CREDS_OPTIONAL}, {"localhost", "localhost", "localhost", CREDS_OPTIONAL},
{"localhost:8080", "localhost:8080", "localhost", CREDS_OPTIONAL}, {"localhost:8080", "localhost:8080", "localhost:8080", CREDS_OPTIONAL},
{"unix:127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_OPTIONAL}, {"unix:127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_OPTIONAL},
{"unix:127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1", CREDS_OPTIONAL}, {"unix:127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CREDS_OPTIONAL},
{"unix://127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_OPTIONAL}, {"unix://127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_OPTIONAL},
{"unix://127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1", CREDS_OPTIONAL}, {"unix://127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CREDS_OPTIONAL},
{"unixs:127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_REQUIRE}, {"unixs:127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_REQUIRE},
{"unixs:127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1", CREDS_REQUIRE}, {"unixs:127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CREDS_REQUIRE},
{"unixs://127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_REQUIRE}, {"unixs://127.0.0.1", "unix:127.0.0.1", "127.0.0.1", CREDS_REQUIRE},
{"unixs://127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1", CREDS_REQUIRE}, {"unixs://127.0.0.1:8080", "unix:127.0.0.1:8080", "127.0.0.1:8080", CREDS_REQUIRE},
{"http://127.0.0.1", "127.0.0.1", "127.0.0.1", CREDS_DROP}, {"http://127.0.0.1", "127.0.0.1", "127.0.0.1", CREDS_DROP},
{"http://127.0.0.1:8080", "127.0.0.1:8080", "127.0.0.1", CREDS_DROP}, {"http://127.0.0.1:8080", "127.0.0.1:8080", "127.0.0.1:8080", CREDS_DROP},
{"https://127.0.0.1", "127.0.0.1", "127.0.0.1", CREDS_REQUIRE}, {"https://127.0.0.1", "127.0.0.1", "127.0.0.1", CREDS_REQUIRE},
{"https://127.0.0.1:8080", "127.0.0.1:8080", "127.0.0.1", CREDS_REQUIRE}, {"https://127.0.0.1:8080", "127.0.0.1:8080", "127.0.0.1:8080", CREDS_REQUIRE},
{"https://localhost:20000", "localhost:20000", "localhost", CREDS_REQUIRE}, {"https://localhost:20000", "localhost:20000", "localhost:20000", CREDS_REQUIRE},
{"unix:///tmp/abc", "unix:///tmp/abc", "abc", CREDS_OPTIONAL}, {"unix:///tmp/abc", "unix:///tmp/abc", "abc", CREDS_OPTIONAL},
{"unixs:///tmp/abc", "unix:///tmp/abc", "abc", CREDS_REQUIRE}, {"unixs:///tmp/abc", "unix:///tmp/abc", "abc", CREDS_REQUIRE},
{"unix:///tmp/abc:1234", "unix:///tmp/abc:1234", "abc", CREDS_OPTIONAL}, {"unix:///tmp/abc:1234", "unix:///tmp/abc:1234", "abc:1234", CREDS_OPTIONAL},
{"unixs:///tmp/abc:1234", "unix:///tmp/abc:1234", "abc", CREDS_REQUIRE}, {"unixs:///tmp/abc:1234", "unix:///tmp/abc:1234", "abc:1234", CREDS_REQUIRE},
{"etcd.io", "etcd.io", "etcd.io", CREDS_OPTIONAL}, {"etcd.io", "etcd.io", "etcd.io", CREDS_OPTIONAL},
{"http://etcd.io/abc", "etcd.io", "etcd.io", CREDS_DROP}, {"http://etcd.io/abc", "etcd.io", "etcd.io", CREDS_DROP},
{"dns://something-other", "dns://something-other", "something-other", CREDS_OPTIONAL}, {"dns://something-other", "dns://something-other", "something-other", CREDS_OPTIONAL},
{"http://[2001:db8:1f70::999:de8:7648:6e8]:100/", "[2001:db8:1f70::999:de8:7648:6e8]:100", "2001:db8:1f70::999:de8:7648:6e8", CREDS_DROP}, {"http://[2001:db8:1f70::999:de8:7648:6e8]:100/", "[2001:db8:1f70::999:de8:7648:6e8]:100", "[2001:db8:1f70::999:de8:7648:6e8]:100", CREDS_DROP},
{"[2001:db8:1f70::999:de8:7648:6e8]:100", "[2001:db8:1f70::999:de8:7648:6e8]:100", "2001:db8:1f70::999:de8:7648:6e8", CREDS_OPTIONAL}, {"[2001:db8:1f70::999:de8:7648:6e8]:100", "[2001:db8:1f70::999:de8:7648:6e8]:100", "[2001:db8:1f70::999:de8:7648:6e8]:100", CREDS_OPTIONAL},
{"unix:unexpected-file_name#123$456", "unix:unexpected-file_name#123$456", "unexpected-file_name#123$456", CREDS_OPTIONAL}, {"unix:unexpected-file_name#123$456", "unix:unexpected-file_name#123$456", "unexpected-file_name#123$456", CREDS_OPTIONAL},
} }
for _, tt := range tests { for _, tt := range tests {

View File

@ -19,6 +19,7 @@ package e2e
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -127,20 +128,21 @@ func TestAuthority(t *testing.T) {
t.Fatalf("could not start etcd process cluster (%v)", err) t.Fatalf("could not start etcd process cluster (%v)", err)
} }
defer epc.Close() defer epc.Close()
endpoints := templateEndpoints(t, tc.clientURLPattern, epc)
endpoints := templateEndpoints(t, tc.clientURLPattern, epc)
client, err := e2e.NewEtcdctl(cfg.Client, endpoints) client, err := e2e.NewEtcdctl(cfg.Client, endpoints)
assert.NoError(t, err) assert.NoError(t, err)
err = client.Put(ctx, "foo", "bar", config.PutOptions{}) for i := 0; i < 100; i++ {
if err != nil { err = client.Put(ctx, "foo", "bar", config.PutOptions{})
t.Fatal(err) if err != nil {
t.Fatal(err)
}
} }
testutils.ExecuteWithTimeout(t, 5*time.Second, func() { testutils.ExecuteWithTimeout(t, 5*time.Second, func() {
assertAuthority(t, strings.ReplaceAll(tc.expectAuthorityPattern, "${MEMBER_PORT}", "20000"), epc) assertAuthority(t, tc.expectAuthorityPattern, epc)
}) })
}) })
} }
} }
} }
@ -156,26 +158,18 @@ func templateEndpoints(t *testing.T, pattern string, clus *e2e.EtcdProcessCluste
return endpoints return endpoints
} }
func assertAuthority(t *testing.T, expectAurhority string, clus *e2e.EtcdProcessCluster) { func assertAuthority(t *testing.T, expectAuthorityPattern string, clus *e2e.EtcdProcessCluster) {
var logs []e2e.LogsExpect for i := range clus.Procs {
for _, proc := range clus.Procs { line, _ := clus.Procs[i].Logs().ExpectWithContext(context.TODO(), `http2: decoded hpack field header field ":authority"`)
logs = append(logs, proc.Logs()) line = strings.TrimSuffix(line, "\n")
} line = strings.TrimSuffix(line, "\r")
line := firstMatch(t, `http2: decoded hpack field header field ":authority"`, logs...)
line = strings.TrimSuffix(line, "\n")
line = strings.TrimSuffix(line, "\r")
expectLine := fmt.Sprintf(`http2: decoded hpack field header field ":authority" = %q`, expectAurhority)
assert.True(t, strings.HasSuffix(line, expectLine), fmt.Sprintf("Got %q expected suffix %q", line, expectLine))
}
func firstMatch(t *testing.T, expectLine string, logs ...e2e.LogsExpect) string { u, err := url.Parse(clus.Procs[i].EndpointsGRPC()[0])
t.Helper() if err != nil {
match := make(chan string, len(logs)) t.Fatal(err)
for i := range logs { }
go func(l e2e.LogsExpect) { expectAuthority := strings.ReplaceAll(expectAuthorityPattern, "${MEMBER_PORT}", u.Port())
line, _ := l.ExpectWithContext(context.TODO(), expectLine) expectLine := fmt.Sprintf(`http2: decoded hpack field header field ":authority" = %q`, expectAuthority)
match <- line assert.True(t, strings.HasSuffix(line, expectLine), fmt.Sprintf("Got %q expected suffix %q", line, expectLine))
}(logs[i])
} }
return <-match
} }

View File

@ -103,12 +103,14 @@ func TestAuthority(t *testing.T) {
defer kv.Close() defer kv.Close()
putRequestMethod := "/etcdserverpb.KV/Put" putRequestMethod := "/etcdserverpb.KV/Put"
_, err := kv.Put(context.TODO(), "foo", "bar") for i := 0; i < 100; i++ {
if err != nil { _, err := kv.Put(context.TODO(), "foo", "bar")
t.Fatal(err) if err != nil {
t.Fatal(err)
}
} }
assertAuthority(t, templateAuthority(t, tc.expectAuthorityPattern, clus.Members[0]), clus, putRequestMethod) assertAuthority(t, tc.expectAuthorityPattern, clus, putRequestMethod)
}) })
} }
} }
@ -162,21 +164,23 @@ func templateAuthority(t *testing.T, pattern string, m *integration.Member) stri
return authority return authority
} }
func assertAuthority(t *testing.T, expectedAuthority string, clus *integration.Cluster, filterMethod string) { func assertAuthority(t *testing.T, expectedAuthorityPattern string, clus *integration.Cluster, filterMethod string) {
t.Helper() t.Helper()
requestsFound := 0
for _, m := range clus.Members { for _, m := range clus.Members {
requestsFound := 0
expectedAuthority := templateAuthority(t, expectedAuthorityPattern, m)
for _, r := range m.RecordedRequests() { for _, r := range m.RecordedRequests() {
if filterMethod != "" && r.FullMethod != filterMethod { if filterMethod != "" && r.FullMethod != filterMethod {
continue continue
} }
requestsFound++ if r.Authority == expectedAuthority {
if r.Authority != expectedAuthority { requestsFound++
} else {
t.Errorf("Got unexpected authority header, member: %q, request: %q, got authority: %q, expected %q", m.Name, r.FullMethod, r.Authority, expectedAuthority) t.Errorf("Got unexpected authority header, member: %q, request: %q, got authority: %q, expected %q", m.Name, r.FullMethod, r.Authority, expectedAuthority)
} }
} }
} if requestsFound == 0 {
if requestsFound == 0 { t.Errorf("Expect at least one request with matched authority header value was recorded by the server intercepter on member %s but got 0", m.Name)
t.Errorf("Expected at least one request") }
} }
} }