2018-03-29 15:25:11 +00:00
|
|
|
package connect
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2018-04-03 18:10:59 +00:00
|
|
|
"crypto/tls"
|
2018-03-29 15:25:11 +00:00
|
|
|
"fmt"
|
2018-04-03 18:10:59 +00:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2018-03-29 15:25:11 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/agent/connect"
|
2018-04-03 18:10:59 +00:00
|
|
|
"github.com/hashicorp/consul/testutil/retry"
|
2018-03-29 15:25:11 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2018-04-03 18:10:59 +00:00
|
|
|
// Assert io.Closer implementation
|
|
|
|
var _ io.Closer = new(Service)
|
|
|
|
|
2018-03-29 15:25:11 +00:00
|
|
|
func TestService_Dial(t *testing.T) {
|
|
|
|
ca := connect.TestCA(t, nil)
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
accept bool
|
|
|
|
handshake bool
|
|
|
|
presentService string
|
|
|
|
wantErr string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "working",
|
|
|
|
accept: true,
|
|
|
|
handshake: true,
|
|
|
|
presentService: "db",
|
|
|
|
wantErr: "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "tcp connect fail",
|
|
|
|
accept: false,
|
|
|
|
handshake: false,
|
|
|
|
presentService: "db",
|
|
|
|
wantErr: "connection refused",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "handshake timeout",
|
|
|
|
accept: true,
|
|
|
|
handshake: false,
|
|
|
|
presentService: "db",
|
|
|
|
wantErr: "i/o timeout",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "bad cert",
|
|
|
|
accept: true,
|
|
|
|
handshake: true,
|
|
|
|
presentService: "web",
|
|
|
|
wantErr: "peer certificate mismatch",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
require := require.New(t)
|
|
|
|
|
2018-04-03 18:10:59 +00:00
|
|
|
s := TestService(t, "web", ca)
|
2018-03-29 15:25:11 +00:00
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(),
|
|
|
|
100*time.Millisecond)
|
|
|
|
defer cancel()
|
|
|
|
|
2018-04-03 18:10:59 +00:00
|
|
|
testSvr := NewTestServer(t, tt.presentService, ca)
|
|
|
|
testSvr.TimeoutHandshake = !tt.handshake
|
2018-03-29 15:25:11 +00:00
|
|
|
|
|
|
|
if tt.accept {
|
|
|
|
go func() {
|
2018-04-03 18:10:59 +00:00
|
|
|
err := testSvr.Serve()
|
2018-03-29 15:25:11 +00:00
|
|
|
require.Nil(err)
|
|
|
|
}()
|
2018-04-03 18:10:59 +00:00
|
|
|
defer testSvr.Close()
|
2018-03-29 15:25:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Always expect to be connecting to a "DB"
|
|
|
|
resolver := &StaticResolver{
|
2018-04-03 18:10:59 +00:00
|
|
|
Addr: testSvr.Addr,
|
2018-03-29 15:25:11 +00:00
|
|
|
CertURI: connect.TestSpiffeIDService(t, "db"),
|
|
|
|
}
|
|
|
|
|
|
|
|
// All test runs should complete in under 500ms due to the timeout about.
|
|
|
|
// Don't wait for whole test run to get stuck.
|
|
|
|
testTimeout := 500 * time.Millisecond
|
|
|
|
testTimer := time.AfterFunc(testTimeout, func() {
|
|
|
|
panic(fmt.Sprintf("test timed out after %s", testTimeout))
|
|
|
|
})
|
|
|
|
|
|
|
|
conn, err := s.Dial(ctx, resolver)
|
|
|
|
testTimer.Stop()
|
|
|
|
|
|
|
|
if tt.wantErr == "" {
|
|
|
|
require.Nil(err)
|
2018-04-03 18:10:59 +00:00
|
|
|
require.IsType(&tls.Conn{}, conn)
|
2018-03-29 15:25:11 +00:00
|
|
|
} else {
|
|
|
|
require.NotNil(err)
|
|
|
|
require.Contains(err.Error(), tt.wantErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
conn.Close()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-04-03 18:10:59 +00:00
|
|
|
|
|
|
|
func TestService_ServerTLSConfig(t *testing.T) {
|
|
|
|
// TODO(banks): it's mostly meaningless to test this now since we directly set
|
|
|
|
// the tlsCfg in our TestService helper which is all we'd be asserting on here
|
|
|
|
// not the actual implementation. Once agent tls fetching is built, it becomes
|
|
|
|
// more meaningful to actually verify it's returning the correct config.
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestService_HTTPClient(t *testing.T) {
|
|
|
|
require := require.New(t)
|
|
|
|
ca := connect.TestCA(t, nil)
|
|
|
|
|
|
|
|
s := TestService(t, "web", ca)
|
|
|
|
|
|
|
|
// Run a test HTTP server
|
|
|
|
testSvr := NewTestServer(t, "backend", ca)
|
|
|
|
defer testSvr.Close()
|
|
|
|
go func() {
|
|
|
|
err := testSvr.ServeHTTPS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Write([]byte("Hello, I am Backend"))
|
|
|
|
}))
|
|
|
|
require.Nil(t, err)
|
|
|
|
}()
|
|
|
|
|
|
|
|
// TODO(banks): this will talk http2 on both client and server. I hit some
|
|
|
|
// compatibility issues when testing though need to make sure that the http
|
|
|
|
// server with our TLSConfig can actually support HTTP/1.1 as well. Could make
|
|
|
|
// this a table test with all 4 permutations of client/server http version
|
|
|
|
// support.
|
|
|
|
|
|
|
|
// Still get connection refused some times so retry on those
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
// Hook the service resolver to avoid needing full agent setup.
|
|
|
|
s.httpResolverFromAddr = func(addr string) (Resolver, error) {
|
|
|
|
// Require in this goroutine seems to block causing a timeout on the Get.
|
|
|
|
//require.Equal("https://backend.service.consul:443", addr)
|
|
|
|
return &StaticResolver{
|
|
|
|
Addr: testSvr.Addr,
|
|
|
|
CertURI: connect.TestSpiffeIDService(t, "backend"),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
client := s.HTTPClient()
|
|
|
|
client.Timeout = 1 * time.Second
|
|
|
|
|
|
|
|
resp, err := client.Get("https://backend.service.consul/foo")
|
|
|
|
r.Check(err)
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
|
|
r.Check(err)
|
|
|
|
|
|
|
|
got := string(bodyBytes)
|
|
|
|
want := "Hello, I am Backend"
|
|
|
|
if got != want {
|
|
|
|
r.Fatalf("got %s, want %s", got, want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|