1084 lines
29 KiB
Go
1084 lines
29 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package configgrpc
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zaptest/observer"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/metadata"
|
|
"google.golang.org/grpc/peer"
|
|
|
|
"go.opentelemetry.io/collector/client"
|
|
"go.opentelemetry.io/collector/component"
|
|
"go.opentelemetry.io/collector/component/componenttest"
|
|
"go.opentelemetry.io/collector/config/configauth"
|
|
"go.opentelemetry.io/collector/config/configcompression"
|
|
"go.opentelemetry.io/collector/config/confignet"
|
|
"go.opentelemetry.io/collector/config/configtls"
|
|
"go.opentelemetry.io/collector/obsreport/obsreporttest"
|
|
"go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp"
|
|
)
|
|
|
|
func TestDefaultGrpcClientSettings(t *testing.T) {
|
|
tt, err := obsreporttest.SetupTelemetry()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
|
|
|
|
gcs := &GRPCClientSettings{
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: true,
|
|
},
|
|
}
|
|
opts, err := gcs.toDialOptions(componenttest.NewNopHost(), tt.TelemetrySettings)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, opts, 3)
|
|
}
|
|
|
|
func TestAllGrpcClientSettings(t *testing.T) {
|
|
tt, err := obsreporttest.SetupTelemetry()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
|
|
|
|
tests := []struct {
|
|
settings GRPCClientSettings
|
|
name string
|
|
host component.Host
|
|
}{
|
|
{
|
|
name: "test all with gzip compression",
|
|
settings: GRPCClientSettings{
|
|
Headers: map[string]string{
|
|
"test": "test",
|
|
},
|
|
Endpoint: "localhost:1234",
|
|
Compression: configcompression.Gzip,
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: false,
|
|
},
|
|
Keepalive: &KeepaliveClientConfig{
|
|
Time: time.Second,
|
|
Timeout: time.Second,
|
|
PermitWithoutStream: true,
|
|
},
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
WaitForReady: true,
|
|
BalancerName: "round_robin",
|
|
Auth: &configauth.Authentication{AuthenticatorID: component.NewID("testauth")},
|
|
},
|
|
host: &mockHost{
|
|
ext: map[component.ID]component.Extension{
|
|
component.NewID("testauth"): &configauth.MockClientAuthenticator{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test all with snappy compression",
|
|
settings: GRPCClientSettings{
|
|
Headers: map[string]string{
|
|
"test": "test",
|
|
},
|
|
Endpoint: "localhost:1234",
|
|
Compression: configcompression.Snappy,
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: false,
|
|
},
|
|
Keepalive: &KeepaliveClientConfig{
|
|
Time: time.Second,
|
|
Timeout: time.Second,
|
|
PermitWithoutStream: true,
|
|
},
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
WaitForReady: true,
|
|
BalancerName: "round_robin",
|
|
Auth: &configauth.Authentication{AuthenticatorID: component.NewID("testauth")},
|
|
},
|
|
host: &mockHost{
|
|
ext: map[component.ID]component.Extension{
|
|
component.NewID("testauth"): &configauth.MockClientAuthenticator{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test all with zstd compression",
|
|
settings: GRPCClientSettings{
|
|
Headers: map[string]string{
|
|
"test": "test",
|
|
},
|
|
Endpoint: "localhost:1234",
|
|
Compression: configcompression.Zstd,
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: false,
|
|
},
|
|
Keepalive: &KeepaliveClientConfig{
|
|
Time: time.Second,
|
|
Timeout: time.Second,
|
|
PermitWithoutStream: true,
|
|
},
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
WaitForReady: true,
|
|
BalancerName: "round_robin",
|
|
Auth: &configauth.Authentication{AuthenticatorID: component.NewID("testauth")},
|
|
},
|
|
host: &mockHost{
|
|
ext: map[component.ID]component.Extension{
|
|
component.NewID("testauth"): &configauth.MockClientAuthenticator{},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
opts, err := test.settings.toDialOptions(test.host, tt.TelemetrySettings)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, opts, 9)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDefaultGrpcServerSettings(t *testing.T) {
|
|
gss := &GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "0.0.0.0:1234",
|
|
},
|
|
}
|
|
opts, err := gss.toServerOption(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings())
|
|
assert.NoError(t, err)
|
|
assert.Len(t, opts, 2)
|
|
}
|
|
|
|
func TestAllGrpcServerSettingsExceptAuth(t *testing.T) {
|
|
gss := &GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "localhost:1234",
|
|
Transport: "tcp",
|
|
},
|
|
TLSSetting: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{},
|
|
ClientCAFile: "",
|
|
},
|
|
MaxRecvMsgSizeMiB: 1,
|
|
MaxConcurrentStreams: 1024,
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
Keepalive: &KeepaliveServerConfig{
|
|
ServerParameters: &KeepaliveServerParameters{
|
|
MaxConnectionIdle: time.Second,
|
|
MaxConnectionAge: time.Second,
|
|
MaxConnectionAgeGrace: time.Second,
|
|
Time: time.Second,
|
|
Timeout: time.Second,
|
|
},
|
|
EnforcementPolicy: &KeepaliveEnforcementPolicy{
|
|
MinTime: time.Second,
|
|
PermitWithoutStream: true,
|
|
},
|
|
},
|
|
}
|
|
opts, err := gss.toServerOption(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings())
|
|
assert.NoError(t, err)
|
|
assert.Len(t, opts, 9)
|
|
}
|
|
|
|
func TestGrpcServerAuthSettings(t *testing.T) {
|
|
gss := &GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "0.0.0.0:1234",
|
|
},
|
|
}
|
|
gss.Auth = &configauth.Authentication{
|
|
AuthenticatorID: component.NewID("mock"),
|
|
}
|
|
host := &mockHost{
|
|
ext: map[component.ID]component.Extension{
|
|
component.NewID("mock"): configauth.NewServerAuthenticator(),
|
|
},
|
|
}
|
|
srv, err := gss.ToServer(host, componenttest.NewNopTelemetrySettings())
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, srv)
|
|
}
|
|
|
|
func TestGRPCClientSettingsError(t *testing.T) {
|
|
tt, err := obsreporttest.SetupTelemetry()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
|
|
|
|
tests := []struct {
|
|
settings GRPCClientSettings
|
|
err string
|
|
host component.Host
|
|
}{
|
|
{
|
|
err: "^failed to load TLS config: failed to load CA CertPool: failed to load CA /doesnt/exist:",
|
|
settings: GRPCClientSettings{
|
|
Headers: nil,
|
|
Endpoint: "",
|
|
Compression: "",
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: "/doesnt/exist",
|
|
},
|
|
Insecure: false,
|
|
ServerName: "",
|
|
},
|
|
Keepalive: nil,
|
|
},
|
|
},
|
|
{
|
|
err: "^failed to load TLS config: for auth via TLS, either both certificate and key must be supplied, or neither",
|
|
settings: GRPCClientSettings{
|
|
Headers: nil,
|
|
Endpoint: "",
|
|
Compression: "",
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CertFile: "/doesnt/exist",
|
|
},
|
|
Insecure: false,
|
|
ServerName: "",
|
|
},
|
|
Keepalive: nil,
|
|
},
|
|
},
|
|
{
|
|
err: "invalid balancer_name: test",
|
|
settings: GRPCClientSettings{
|
|
Headers: map[string]string{
|
|
"test": "test",
|
|
},
|
|
Endpoint: "localhost:1234",
|
|
Compression: "gzip",
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: false,
|
|
},
|
|
Keepalive: &KeepaliveClientConfig{
|
|
Time: time.Second,
|
|
Timeout: time.Second,
|
|
PermitWithoutStream: true,
|
|
},
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
WaitForReady: true,
|
|
BalancerName: "test",
|
|
},
|
|
},
|
|
{
|
|
err: "failed to resolve authenticator \"doesntexist\": authenticator not found",
|
|
settings: GRPCClientSettings{
|
|
Endpoint: "localhost:1234",
|
|
Auth: &configauth.Authentication{AuthenticatorID: component.NewID("doesntexist")},
|
|
},
|
|
host: &mockHost{ext: map[component.ID]component.Extension{}},
|
|
},
|
|
{
|
|
err: "no extensions configuration available",
|
|
settings: GRPCClientSettings{
|
|
Endpoint: "localhost:1234",
|
|
Auth: &configauth.Authentication{AuthenticatorID: component.NewID("doesntexist")},
|
|
},
|
|
host: &mockHost{},
|
|
},
|
|
{
|
|
err: "unsupported compression type \"zlib\"",
|
|
settings: GRPCClientSettings{
|
|
Endpoint: "localhost:1234",
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: true,
|
|
},
|
|
Compression: "zlib",
|
|
},
|
|
host: &mockHost{},
|
|
},
|
|
{
|
|
err: "unsupported compression type \"deflate\"",
|
|
settings: GRPCClientSettings{
|
|
Endpoint: "localhost:1234",
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: true,
|
|
},
|
|
Compression: "deflate",
|
|
},
|
|
host: &mockHost{},
|
|
},
|
|
{
|
|
err: "unsupported compression type \"bad\"",
|
|
settings: GRPCClientSettings{
|
|
Endpoint: "localhost:1234",
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: true,
|
|
},
|
|
Compression: "bad",
|
|
},
|
|
host: &mockHost{},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.err, func(t *testing.T) {
|
|
_, err := test.settings.ToClientConn(context.Background(), test.host, tt.TelemetrySettings)
|
|
assert.Error(t, err)
|
|
assert.Regexp(t, test.err, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUseSecure(t *testing.T) {
|
|
tt, err := obsreporttest.SetupTelemetry()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
|
|
|
|
gcs := &GRPCClientSettings{
|
|
Headers: nil,
|
|
Endpoint: "",
|
|
Compression: "",
|
|
TLSSetting: configtls.TLSClientSetting{},
|
|
Keepalive: nil,
|
|
}
|
|
dialOpts, err := gcs.toDialOptions(componenttest.NewNopHost(), tt.TelemetrySettings)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, dialOpts, 3)
|
|
}
|
|
|
|
func TestGRPCServerWarning(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
settings GRPCServerSettings
|
|
len int
|
|
}{
|
|
{
|
|
settings: GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "0.0.0.0:1234",
|
|
Transport: "tcp",
|
|
},
|
|
},
|
|
len: 1,
|
|
},
|
|
{
|
|
settings: GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "127.0.0.1:1234",
|
|
Transport: "tcp",
|
|
},
|
|
},
|
|
len: 0,
|
|
},
|
|
{
|
|
settings: GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "0.0.0.0:1234",
|
|
Transport: "unix",
|
|
},
|
|
},
|
|
len: 0,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
set := componenttest.NewNopTelemetrySettings()
|
|
logger, observed := observer.New(zap.DebugLevel)
|
|
set.Logger = zap.New(logger)
|
|
|
|
opts, err := test.settings.toServerOption(componenttest.NewNopHost(), set)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, opts)
|
|
_ = grpc.NewServer(opts...)
|
|
|
|
require.Len(t, observed.FilterLevelExact(zap.WarnLevel).All(), test.len)
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestGRPCServerSettingsError(t *testing.T) {
|
|
tests := []struct {
|
|
settings GRPCServerSettings
|
|
err string
|
|
}{
|
|
{
|
|
err: "^failed to load TLS config: failed to load CA CertPool: failed to load CA /doesnt/exist:",
|
|
settings: GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "127.0.0.1:1234",
|
|
Transport: "tcp",
|
|
},
|
|
TLSSetting: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: "/doesnt/exist",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
err: "^failed to load TLS config: for auth via TLS, either both certificate and key must be supplied, or neither",
|
|
settings: GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "127.0.0.1:1234",
|
|
Transport: "tcp",
|
|
},
|
|
TLSSetting: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CertFile: "/doesnt/exist",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
err: "^failed to load TLS config: failed to load client CA CertPool: failed to load CA /doesnt/exist:",
|
|
settings: GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "127.0.0.1:1234",
|
|
Transport: "tcp",
|
|
},
|
|
TLSSetting: &configtls.TLSServerSetting{
|
|
ClientCAFile: "/doesnt/exist",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.err, func(t *testing.T) {
|
|
_, err := test.settings.ToServer(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings())
|
|
assert.Regexp(t, test.err, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerSettings_ToListener_Error(t *testing.T) {
|
|
settings := GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "127.0.0.1:1234567",
|
|
Transport: "tcp",
|
|
},
|
|
TLSSetting: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CertFile: "/doesnt/exist",
|
|
},
|
|
},
|
|
Keepalive: nil,
|
|
}
|
|
_, err := settings.ToListener()
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestHttpReception(t *testing.T) {
|
|
tt, err := obsreporttest.SetupTelemetry()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
|
|
|
|
tests := []struct {
|
|
name string
|
|
tlsServerCreds *configtls.TLSServerSetting
|
|
tlsClientCreds *configtls.TLSClientSetting
|
|
hasError bool
|
|
}{
|
|
{
|
|
name: "noTLS",
|
|
tlsServerCreds: nil,
|
|
tlsClientCreds: &configtls.TLSClientSetting{
|
|
Insecure: true,
|
|
},
|
|
},
|
|
{
|
|
name: "TLS",
|
|
tlsServerCreds: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
CertFile: filepath.Join("testdata", "server.crt"),
|
|
KeyFile: filepath.Join("testdata", "server.key"),
|
|
},
|
|
},
|
|
tlsClientCreds: &configtls.TLSClientSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
},
|
|
ServerName: "localhost",
|
|
},
|
|
},
|
|
{
|
|
name: "NoServerCertificates",
|
|
tlsServerCreds: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
},
|
|
},
|
|
tlsClientCreds: &configtls.TLSClientSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
},
|
|
ServerName: "localhost",
|
|
},
|
|
hasError: true,
|
|
},
|
|
{
|
|
name: "mTLS",
|
|
tlsServerCreds: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
CertFile: filepath.Join("testdata", "server.crt"),
|
|
KeyFile: filepath.Join("testdata", "server.key"),
|
|
},
|
|
ClientCAFile: filepath.Join("testdata", "ca.crt"),
|
|
},
|
|
tlsClientCreds: &configtls.TLSClientSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
CertFile: filepath.Join("testdata", "client.crt"),
|
|
KeyFile: filepath.Join("testdata", "client.key"),
|
|
},
|
|
ServerName: "localhost",
|
|
},
|
|
},
|
|
{
|
|
name: "NoClientCertificate",
|
|
tlsServerCreds: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
CertFile: filepath.Join("testdata", "server.crt"),
|
|
KeyFile: filepath.Join("testdata", "server.key"),
|
|
},
|
|
ClientCAFile: filepath.Join("testdata", "ca.crt"),
|
|
},
|
|
tlsClientCreds: &configtls.TLSClientSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
},
|
|
ServerName: "localhost",
|
|
},
|
|
hasError: true,
|
|
},
|
|
{
|
|
name: "WrongClientCA",
|
|
tlsServerCreds: &configtls.TLSServerSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
CertFile: filepath.Join("testdata", "server.crt"),
|
|
KeyFile: filepath.Join("testdata", "server.key"),
|
|
},
|
|
ClientCAFile: filepath.Join("testdata", "server.crt"),
|
|
},
|
|
tlsClientCreds: &configtls.TLSClientSetting{
|
|
TLSSetting: configtls.TLSSetting{
|
|
CAFile: filepath.Join("testdata", "ca.crt"),
|
|
CertFile: filepath.Join("testdata", "client.crt"),
|
|
KeyFile: filepath.Join("testdata", "client.key"),
|
|
},
|
|
ServerName: "localhost",
|
|
},
|
|
hasError: true,
|
|
},
|
|
}
|
|
// prepare
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
gss := &GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "localhost:0",
|
|
Transport: "tcp",
|
|
},
|
|
TLSSetting: test.tlsServerCreds,
|
|
}
|
|
ln, err := gss.ToListener()
|
|
assert.NoError(t, err)
|
|
s, err := gss.ToServer(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings())
|
|
assert.NoError(t, err)
|
|
ptraceotlp.RegisterGRPCServer(s, &grpcTraceServer{})
|
|
|
|
go func() {
|
|
_ = s.Serve(ln)
|
|
}()
|
|
|
|
gcs := &GRPCClientSettings{
|
|
Endpoint: ln.Addr().String(),
|
|
TLSSetting: *test.tlsClientCreds,
|
|
}
|
|
grpcClientConn, errClient := gcs.ToClientConn(context.Background(), componenttest.NewNopHost(), tt.TelemetrySettings)
|
|
assert.NoError(t, errClient)
|
|
c := ptraceotlp.NewGRPCClient(grpcClientConn)
|
|
ctx, cancelFunc := context.WithTimeout(context.Background(), 2*time.Second)
|
|
resp, errResp := c.Export(ctx, ptraceotlp.NewExportRequest(), grpc.WaitForReady(true))
|
|
if test.hasError {
|
|
assert.Error(t, errResp)
|
|
} else {
|
|
assert.NoError(t, errResp)
|
|
assert.NotNil(t, resp)
|
|
}
|
|
cancelFunc()
|
|
s.Stop()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReceiveOnUnixDomainSocket(t *testing.T) {
|
|
if runtime.GOOS == "windows" {
|
|
t.Skip("skipping test on windows")
|
|
}
|
|
tt, err := obsreporttest.SetupTelemetry()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
|
|
|
|
socketName := tempSocketName(t)
|
|
gss := &GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: socketName,
|
|
Transport: "unix",
|
|
},
|
|
}
|
|
ln, err := gss.ToListener()
|
|
assert.NoError(t, err)
|
|
srv, err := gss.ToServer(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings())
|
|
assert.NoError(t, err)
|
|
ptraceotlp.RegisterGRPCServer(srv, &grpcTraceServer{})
|
|
|
|
go func() {
|
|
_ = srv.Serve(ln)
|
|
}()
|
|
|
|
gcs := &GRPCClientSettings{
|
|
Endpoint: "unix://" + ln.Addr().String(),
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: true,
|
|
},
|
|
}
|
|
grpcClientConn, errClient := gcs.ToClientConn(context.Background(), componenttest.NewNopHost(), tt.TelemetrySettings)
|
|
assert.NoError(t, errClient)
|
|
c := ptraceotlp.NewGRPCClient(grpcClientConn)
|
|
ctx, cancelFunc := context.WithTimeout(context.Background(), 2*time.Second)
|
|
resp, errResp := c.Export(ctx, ptraceotlp.NewExportRequest(), grpc.WaitForReady(true))
|
|
assert.NoError(t, errResp)
|
|
assert.NotNil(t, resp)
|
|
cancelFunc()
|
|
srv.Stop()
|
|
}
|
|
|
|
func TestContextWithClient(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
input context.Context
|
|
doMetadata bool
|
|
expected client.Info
|
|
}{
|
|
{
|
|
desc: "no peer information, empty client",
|
|
input: context.Background(),
|
|
expected: client.Info{},
|
|
},
|
|
{
|
|
desc: "existing client with IP, no peer information",
|
|
input: client.NewContext(context.Background(), client.Info{
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(1, 2, 3, 4),
|
|
},
|
|
}),
|
|
expected: client.Info{
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(1, 2, 3, 4),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
desc: "empty client, with peer information",
|
|
input: peer.NewContext(context.Background(), &peer.Peer{
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(1, 2, 3, 4),
|
|
},
|
|
}),
|
|
expected: client.Info{
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(1, 2, 3, 4),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
desc: "existing client, existing IP gets overridden with peer information",
|
|
input: peer.NewContext(client.NewContext(context.Background(), client.Info{
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(1, 2, 3, 4),
|
|
},
|
|
}), &peer.Peer{
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(1, 2, 3, 5),
|
|
},
|
|
}),
|
|
expected: client.Info{
|
|
Addr: &net.IPAddr{
|
|
IP: net.IPv4(1, 2, 3, 5),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
desc: "existing client with metadata",
|
|
input: client.NewContext(context.Background(), client.Info{
|
|
Metadata: client.NewMetadata(map[string][]string{"test-metadata-key": {"test-value"}}),
|
|
}),
|
|
doMetadata: true,
|
|
expected: client.Info{
|
|
Metadata: client.NewMetadata(map[string][]string{"test-metadata-key": {"test-value"}}),
|
|
},
|
|
},
|
|
{
|
|
desc: "existing client with metadata in context",
|
|
input: metadata.NewIncomingContext(
|
|
client.NewContext(context.Background(), client.Info{}),
|
|
metadata.Pairs("test-metadata-key", "test-value"),
|
|
),
|
|
doMetadata: true,
|
|
expected: client.Info{
|
|
Metadata: client.NewMetadata(map[string][]string{"test-metadata-key": {"test-value"}}),
|
|
},
|
|
},
|
|
{
|
|
desc: "existing client with metadata in context, no metadata processing",
|
|
input: metadata.NewIncomingContext(
|
|
client.NewContext(context.Background(), client.Info{}),
|
|
metadata.Pairs("test-metadata-key", "test-value"),
|
|
),
|
|
expected: client.Info{},
|
|
},
|
|
{
|
|
desc: "existing client with Host and metadata",
|
|
input: metadata.NewIncomingContext(
|
|
client.NewContext(context.Background(), client.Info{}),
|
|
metadata.Pairs("test-metadata-key", "test-value", ":authority", "localhost:55443"),
|
|
),
|
|
doMetadata: true,
|
|
expected: client.Info{
|
|
Metadata: client.NewMetadata(map[string][]string{"test-metadata-key": {"test-value"}, ":authority": {"localhost:55443"}, "Host": {"localhost:55443"}}),
|
|
},
|
|
},
|
|
}
|
|
for _, tC := range testCases {
|
|
t.Run(tC.desc, func(t *testing.T) {
|
|
cl := client.FromContext(contextWithClient(tC.input, tC.doMetadata))
|
|
assert.Equal(t, tC.expected, cl)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStreamInterceptorEnhancesClient(t *testing.T) {
|
|
// prepare
|
|
inCtx := peer.NewContext(context.Background(), &peer.Peer{
|
|
Addr: &net.IPAddr{IP: net.IPv4(1, 1, 1, 1)},
|
|
})
|
|
var outContext context.Context
|
|
|
|
stream := &mockedStream{
|
|
ctx: inCtx,
|
|
}
|
|
|
|
handler := func(srv interface{}, stream grpc.ServerStream) error {
|
|
outContext = stream.Context()
|
|
return nil
|
|
}
|
|
|
|
// test
|
|
err := enhanceStreamWithClientInformation(false)(nil, stream, nil, handler)
|
|
|
|
// verify
|
|
assert.NoError(t, err)
|
|
|
|
cl := client.FromContext(outContext)
|
|
assert.Equal(t, "1.1.1.1", cl.Addr.String())
|
|
}
|
|
|
|
type mockedStream struct {
|
|
ctx context.Context
|
|
grpc.ServerStream
|
|
}
|
|
|
|
func (ms *mockedStream) Context() context.Context {
|
|
return ms.ctx
|
|
}
|
|
|
|
func TestClientInfoInterceptors(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
tester func(context.Context, ptraceotlp.GRPCClient)
|
|
}{
|
|
{
|
|
// we only have unary services, we don't have any clients we could use
|
|
// to test with streaming services
|
|
desc: "unary",
|
|
tester: func(ctx context.Context, cl ptraceotlp.GRPCClient) {
|
|
resp, errResp := cl.Export(ctx, ptraceotlp.NewExportRequest())
|
|
require.NoError(t, errResp)
|
|
require.NotNil(t, resp)
|
|
},
|
|
},
|
|
}
|
|
for _, tC := range testCases {
|
|
t.Run(tC.desc, func(t *testing.T) {
|
|
mock := &grpcTraceServer{}
|
|
var l net.Listener
|
|
|
|
// prepare the server
|
|
{
|
|
gss := &GRPCServerSettings{
|
|
NetAddr: confignet.NetAddr{
|
|
Endpoint: "localhost:0",
|
|
Transport: "tcp",
|
|
},
|
|
}
|
|
srv, err := gss.ToServer(componenttest.NewNopHost(), componenttest.NewNopTelemetrySettings())
|
|
require.NoError(t, err)
|
|
ptraceotlp.RegisterGRPCServer(srv, mock)
|
|
|
|
defer srv.Stop()
|
|
|
|
l, err = gss.ToListener()
|
|
require.NoError(t, err)
|
|
|
|
go func() {
|
|
_ = srv.Serve(l)
|
|
}()
|
|
}
|
|
|
|
// prepare the client and execute a RPC
|
|
{
|
|
gcs := &GRPCClientSettings{
|
|
Endpoint: l.Addr().String(),
|
|
TLSSetting: configtls.TLSClientSetting{
|
|
Insecure: true,
|
|
},
|
|
}
|
|
|
|
tt, err := obsreporttest.SetupTelemetry()
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
require.NoError(t, tt.Shutdown(context.Background()))
|
|
}()
|
|
|
|
grpcClientConn, errClient := gcs.ToClientConn(context.Background(), componenttest.NewNopHost(), tt.TelemetrySettings)
|
|
require.NoError(t, errClient)
|
|
|
|
cl := ptraceotlp.NewGRPCClient(grpcClientConn)
|
|
ctx, cancelFunc := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancelFunc()
|
|
|
|
// test
|
|
tC.tester(ctx, cl)
|
|
}
|
|
|
|
// verify
|
|
cl := client.FromContext(mock.recordedContext)
|
|
|
|
// the client address is something like 127.0.0.1:41086
|
|
assert.Contains(t, cl.Addr.String(), "127.0.0.1")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDefaultUnaryInterceptorAuthSucceeded(t *testing.T) {
|
|
// prepare
|
|
handlerCalled := false
|
|
authCalled := false
|
|
authFunc := func(context.Context, map[string][]string) (context.Context, error) {
|
|
authCalled = true
|
|
ctx := client.NewContext(context.Background(), client.Info{
|
|
Addr: &net.IPAddr{IP: net.IPv4(1, 2, 3, 4)},
|
|
})
|
|
|
|
return ctx, nil
|
|
}
|
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
handlerCalled = true
|
|
cl := client.FromContext(ctx)
|
|
assert.Equal(t, "1.2.3.4", cl.Addr.String())
|
|
return nil, nil
|
|
}
|
|
ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data"))
|
|
|
|
// test
|
|
res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, authFunc)
|
|
|
|
// verify
|
|
assert.Nil(t, res)
|
|
assert.NoError(t, err)
|
|
assert.True(t, authCalled)
|
|
assert.True(t, handlerCalled)
|
|
}
|
|
|
|
func TestDefaultUnaryInterceptorAuthFailure(t *testing.T) {
|
|
// prepare
|
|
authCalled := false
|
|
expectedErr := errors.New("not authenticated")
|
|
authFunc := func(context.Context, map[string][]string) (context.Context, error) {
|
|
authCalled = true
|
|
return context.Background(), expectedErr
|
|
}
|
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
assert.FailNow(t, "the handler should not have been called on auth failure!")
|
|
return nil, nil
|
|
}
|
|
ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data"))
|
|
|
|
// test
|
|
res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, authFunc)
|
|
|
|
// verify
|
|
assert.Nil(t, res)
|
|
assert.Equal(t, expectedErr, err)
|
|
assert.True(t, authCalled)
|
|
}
|
|
|
|
func TestDefaultUnaryInterceptorMissingMetadata(t *testing.T) {
|
|
// prepare
|
|
authFunc := func(context.Context, map[string][]string) (context.Context, error) {
|
|
assert.FailNow(t, "the auth func should not have been called!")
|
|
return context.Background(), nil
|
|
}
|
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
assert.FailNow(t, "the handler should not have been called!")
|
|
return nil, nil
|
|
}
|
|
|
|
// test
|
|
res, err := authUnaryServerInterceptor(context.Background(), nil, &grpc.UnaryServerInfo{}, handler, authFunc)
|
|
|
|
// verify
|
|
assert.Nil(t, res)
|
|
assert.Equal(t, errMetadataNotFound, err)
|
|
}
|
|
|
|
func TestDefaultStreamInterceptorAuthSucceeded(t *testing.T) {
|
|
// prepare
|
|
handlerCalled := false
|
|
authCalled := false
|
|
authFunc := func(context.Context, map[string][]string) (context.Context, error) {
|
|
authCalled = true
|
|
ctx := client.NewContext(context.Background(), client.Info{
|
|
Addr: &net.IPAddr{IP: net.IPv4(1, 2, 3, 4)},
|
|
})
|
|
return ctx, nil
|
|
}
|
|
handler := func(srv interface{}, stream grpc.ServerStream) error {
|
|
// ensure that the client information is propagated down to the underlying stream
|
|
cl := client.FromContext(stream.Context())
|
|
assert.Equal(t, "1.2.3.4", cl.Addr.String())
|
|
handlerCalled = true
|
|
return nil
|
|
}
|
|
ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data"))
|
|
streamServer := &mockServerStream{
|
|
ctx: ctx,
|
|
}
|
|
|
|
// test
|
|
err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc)
|
|
|
|
// verify
|
|
assert.NoError(t, err)
|
|
assert.True(t, authCalled)
|
|
assert.True(t, handlerCalled)
|
|
}
|
|
|
|
func TestDefaultStreamInterceptorAuthFailure(t *testing.T) {
|
|
// prepare
|
|
authCalled := false
|
|
expectedErr := errors.New("not authenticated")
|
|
authFunc := func(context.Context, map[string][]string) (context.Context, error) {
|
|
authCalled = true
|
|
return context.Background(), expectedErr
|
|
}
|
|
handler := func(srv interface{}, stream grpc.ServerStream) error {
|
|
assert.FailNow(t, "the handler should not have been called on auth failure!")
|
|
return nil
|
|
}
|
|
ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data"))
|
|
streamServer := &mockServerStream{
|
|
ctx: ctx,
|
|
}
|
|
|
|
// test
|
|
err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc)
|
|
|
|
// verify
|
|
assert.Equal(t, expectedErr, err)
|
|
assert.True(t, authCalled)
|
|
}
|
|
|
|
func TestDefaultStreamInterceptorMissingMetadata(t *testing.T) {
|
|
// prepare
|
|
authFunc := func(context.Context, map[string][]string) (context.Context, error) {
|
|
assert.FailNow(t, "the auth func should not have been called!")
|
|
return context.Background(), nil
|
|
}
|
|
handler := func(srv interface{}, stream grpc.ServerStream) error {
|
|
assert.FailNow(t, "the handler should not have been called!")
|
|
return nil
|
|
}
|
|
streamServer := &mockServerStream{
|
|
ctx: context.Background(),
|
|
}
|
|
|
|
// test
|
|
err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc)
|
|
|
|
// verify
|
|
assert.Equal(t, errMetadataNotFound, err)
|
|
}
|
|
|
|
type mockServerStream struct {
|
|
grpc.ServerStream
|
|
ctx context.Context
|
|
}
|
|
|
|
func (m *mockServerStream) Context() context.Context {
|
|
return m.ctx
|
|
}
|
|
|
|
type grpcTraceServer struct {
|
|
recordedContext context.Context
|
|
}
|
|
|
|
func (gts *grpcTraceServer) Export(ctx context.Context, _ ptraceotlp.ExportRequest) (ptraceotlp.ExportResponse, error) {
|
|
gts.recordedContext = ctx
|
|
return ptraceotlp.NewExportResponse(), nil
|
|
}
|
|
|
|
// tempSocketName provides a temporary Unix socket name for testing.
|
|
func tempSocketName(t *testing.T) string {
|
|
tmpfile, err := os.CreateTemp("", "sock")
|
|
require.NoError(t, err)
|
|
require.NoError(t, tmpfile.Close())
|
|
socket := tmpfile.Name()
|
|
require.NoError(t, os.Remove(socket))
|
|
return socket
|
|
}
|
|
|
|
type mockHost struct {
|
|
component.Host
|
|
ext map[component.ID]component.Extension
|
|
}
|
|
|
|
func (nh *mockHost) GetExtensions() map[component.ID]component.Extension {
|
|
return nh.ext
|
|
}
|